1.Data exploration + Logistic Regression
a.Describe the data: sample size n, number of predictors p,
and number of observations in each class.
- n(sample size): 569
- number of predictors: 2
- number of observations in each class: M=212; B=357
##------------------------------------
# a.
wdbc %>% summarise(n=n())
wdbc %>% group_by(V2) %>% summarise(n=n(), )
glimpse(wdbc)
b.Divide the data into a training set of 400 observations,
and a test set; from now on, unless specified, work only on the training
set.
##------------------
## b.
## Divide the data into a training set of 400 obs
set.seed(101)
train_id_wdbc = sample(nrow(wdbc), 400)
train_wdbc = wdbc[train_id_wdbc, ]
test_wdbc = wdbc[-train_id_wdbc, ]
c.Make a scatterplot displaying Y (color or shape encoded)
and the predic- tors X1, X2 (on the x- and y-axis). Based on this
scatterplot, do you think it will be possible to accurately predict the
outcome from the predictors? Motivate your answer.
Based on the plot, we conclude that the average radius is a better
predictor for the diagnosis as the blue points (diagnosis with
malignant) tend to have higher values and the red points (diagnosis with
benign) tend to have lower values, while the average texture does not
seem to be a strong predictor as the red points and the blue points are
spread evenly.

d.Fit a logistic regression model to predict Y and make a
table, displaying the coefficient estimates, standard errors, and
p-values (use command summary). Give an interpretation of the values of
the coefficient estimates.
The estimated expected log-odds of diagnosis with malignant for a
population with average radium = 0 and average texture = 0 is
-21.381
The log-odds of diagnosis with malignant is estimated to be 1.123
times greater for a population with one more unit increase in the
average radius of the cell nuclei, compared to a population of the same
average texture of the cell nuclei.
The log-odds of the diagnosis with malignant is estimated to be
0.247 times greater for a population with one more unit increase in the
average texture of the cell nuclei, compared to a population of the same
average radius of the cell nuclei.
##------------------
## logistic model
## transfer M variable from character to numeric (0,1)
train_wdbc$Dig <- ifelse (train_wdbc$V2=="M", 1, 0)
glm.wdbc = glm(Dig ~ V3 + V4, family = binomial(link = "logit"), data = train_wdbc)
summary(glm.wdbc)
kable(summary(glm.wdbc)$coefficients)
| (Intercept) |
-21.3813316 |
2.3238811 |
-9.200699 |
0e+00 |
| V3 |
1.1232747 |
0.1304202 |
8.612734 |
0e+00 |
| V4 |
0.2474737 |
0.0485324 |
5.099144 |
3e-07 |
e.Use the coefficient estimates to manually calculate the
predicted probabil- ity P(Y = M | (X1,X2) = (10,12)) writing explicitly
every step. Compare your results with the prediction computed with
predict.
manually calculate: \(prediction =
-21.381 + 1.123*10 + 0.247*12 = -7.178901\), \(probability = 1/(1+exp(-7.178901) =
0.00076\)
predict(glm.wdbc, data.frame(V3 = 10, V4 = 12), type =
“response”) = 0.00076
Therefore, the two results are the same.
##------------------
## e.
## extract the coefficients
beta_0 <- coef(glm.wdbc)[1]
beta_1 <- coef(glm.wdbc)[2]
beta_2 <- coef(glm.wdbc)[3]
## calculate the linear predictor
pred <- beta_0 + beta_1*10 + beta_2*12
## calculate the predicted probability
prob <- 1 / (1 + exp(-pred))
prob
# 0.0007619246
# compare with the prediction computed using predict
predict(glm.wdbc, data.frame(V3 = 10, V4 = 12), type = "response")
# 0.0007619246
# The two values are the same.
f.Use the glm previously fitted and the Bayes rule to compute
the pre- dicted outcome Yˆ from the associated probability estimates
(computed with predict) both on the training and the test set. Then
compute the confusion table and prediction accuracy (rate of correctly
classified obser- vations) both on the training and test set. Comment on
the results.
The model accuracy for training data and the one for testing data are
similar. It indicates that the good model performance.
##------------------
## f.
## Compute the predicted probabilities using the predict function
train_probs <- predict(glm.wdbc, newdata = train_wdbc, type = "response")
test_probs <- predict(glm.wdbc, newdata = test_wdbc, type = "response")
## Use the Bayes rule to compute the predicted outcome
train_prediction <- ifelse(train_probs > 0.5, "M", "B")
test_prediction <- ifelse(test_probs > 0.5, "M", "B")
## Create confusion tables on the training and test sets
train_confusion_table <- table(Prediction = train_prediction, Actual = train_wdbc$V2)
test_confusion_table <- table(Prediction = test_prediction, Actual = test_wdbc$V2)
## Calculate prediction accuracy on the training and test sets
train_accuracy <- sum(diag(train_confusion_table)) / nrow(train_wdbc)
test_accuracy <- sum(diag(test_confusion_table)) / nrow(test_wdbc)
kable(train_confusion_table,
caption = "Confusion Table for training set")
Confusion Table for training set
| B |
233 |
23 |
| M |
15 |
129 |
kable(test_confusion_table,
caption = "Confusion Table for testing set")
Confusion Table for testing set
| B |
101 |
12 |
| M |
8 |
48 |
g.Plot an image of the decision boundary as
follows:1.Generate a dense set (e.g. 10000 observations) of possible
values for the predictors (X1,X2) within reasonable ranges; (the command
expand.grid might come in handy); 2.Use the glm model previously fitted
to predict the outcome probabil- ities for every observation you have
generated and use Bayes rule to compute the predicted outcomes; 3.Plot
predicted outcomes (color coded) and associated predictors in a 2D
scatter plot together with the training set.Generate the same plot for
probability cutoff values of 0.25 and 0.75. Comment on the
results.
The increase in the red region (predicted as benign tumor) as the
cutoff value increases. When the cutoff value is low, the model would be
more conservative in classifying an observation as benign, while as the
cutoff value increases, the model becomes more confident in classifying
an observation as benign. Therefore, the red region would increase with
higher cutoff value.
## g.
## specify reasonable ranges for X1 and X2
x1_range <- c(min(train_wdbc$V3), max(train_wdbc$V3))
x2_range <- c(min(train_wdbc$V4), max(train_wdbc$V4))
## create a dense set of possible values for X1 and X2 using expand.grid
dense_set <- expand.grid(V3 = seq(from = x1_range[1], to = x1_range[2], length.out = 100),
V4 = seq(from = x2_range[1], to = x2_range[2], length.out = 100))
## use the fitted GLM model to predict the outcome probabilities for each observation in the dense set
dense_set$probs <- predict(glm.wdbc, newdata = dense_set, type = "response")
## compute the predicted outcomes using Bayes rule
dense_set$prediction <- ifelse(dense_set$probs > 0.5, "M", "B")



h.Plot the ROC curve, computed on the test set.

i.Compute an estimate of the Area under the ROC Curve
(AUC).
The AUC score is 0.9398.
2.Linear Discriminant Analysis Model
a.Now fit a linear discriminant analysis model to the
training set you created in Exercise 1. Make a table displaying the
estimated ‘Prior probabilities of groups’ and ‘Group means’. Describe in
words the meaning of these estimates and how they are related to the
posterior probabilities.
The estimated group means are the average of the predictor variables
(radius or texture) for each group (malignant or benign). These
estimates are used to calculate the posterior probabilities which is the
probability of an observation belonging to malignant or benign given its
estimated predictor values. This calculation is based on Bayes’ theorem,
which combines prior knowledge (prior probabilities) and new information
(estimated predictor values) to estimate the probability of an
event.
##------------------------------------ LDA
## a.
## linear discriminant model
lda.wdbc = lda(Dig ~ V3 + V4, data = train_wdbc, center = TRUE)
lda_table <- cbind(lda.wdbc$prior, lda.wdbc$mean)
colnames(lda_table) <- c("Prior prob of groups", "Group means(radius)", "Group means(texture)")
kable(lda_table)
| 0 |
0.62 |
12.15901 |
17.87661 |
| 1 |
0.38 |
17.46270 |
21.68230 |
b.Use the fitted model and Bayes rule to compute the
predicted outcome Yˆ from the predicted posterior probabilities, both on
the training and test set. Then, compute the confusion table and
prediction accuracy both on the training and test set. Comment on the
results.
The two prediction accuracy are similar, it indicates that the model
has a good performance. The values are slightly smaller than the
accuracy from fitting the logistic model. LDA finds the optimal linear
combinations of predictors that maximize the separation between classes
and then uses Bayes’ theorem to make predictions, while logistic
regression uses maximum likelihood estimation to fit a logistic function
to the data. In this scenario, it seems that the logistic model is more
appropriate because LDA with linear combination might be relative
simpler for the data.
##------------------
## b
X_train <- train_wdbc %>% select(c(V3, V4))
X_test <- test_wdbc %>% select(c(V3, V4))
## fit the model and calculate post prob
train_pred.lda <- predict(lda.wdbc, X_train)$posterior
test_pred.lda <- predict(lda.wdbc, X_test)$posterior
## compute outcome Y based on the post prob
train_outcome.lda <- apply(train_pred.lda, 1, which.max)
## outcome variable = 1 or 2
## transfer them to B or M
train_outcome.lda <- data.frame(train_outcome.lda)
train_outcome.lda$train_outcome.lda <-
ifelse(train_outcome.lda$train_outcome.lda==1, "B", "M")
## Do it again, but using testing set
test_outcome.lda <- apply(test_pred.lda, 1, which.max)
## outcome variable = 1 or 2
## transfer them to B or M
test_outcome.lda <- data.frame(test_outcome.lda)
test_outcome.lda$test_outcome.lda <-
ifelse(test_outcome.lda$test_outcome.lda==1, "B", "M")
## confusion matrix
train_confusion.lda <- table(Prediction = train_outcome.lda$train_outcome.lda,
Actual = train_wdbc$V2)
test_confusion.lda <- table(Prediction = test_outcome.lda$test_outcome.lda,
Actual = test_wdbc$V2)
## prediction accuracy
train_accuracy.lda <- sum(diag(train_confusion.lda)) / nrow(train_wdbc)
test_accuracy.lda <- sum(diag(test_confusion.lda)) / nrow(test_wdbc)
kable(train_confusion.lda,
caption = "Confusion table for training set - LDA")
Confusion table for training set - LDA
| B |
241 |
37 |
| M |
7 |
115 |
kable(test_confusion.lda,
caption = "Confusion table for testing set - LDA")
Confusion table for testing set - LDA
| B |
104 |
16 |
| M |
5 |
44 |
c.Plot an image of the LDA decision boundary (following the
steps in 1(g)). Generate the same plot for cutoff values of 0.25 and
0.75. Comment on the results.
The increase in the red region (predicted as benign tumor) as the
cutoff value decreases When the cutoff value is high, the model would be
more conservative in classifying an observation as benign, while as the
cutoff value decrease, the model becomes more confident in classifying
an observation as benign.
##------------------
## c.
## use the fitted model to predict the outcome probabilities for each observation in the dense set
probs.lda <- predict(lda.wdbc, newdata = dense_set, type = "response")$posterior
# Compute the predicted outcomes using Bayes rule with different probability cutoff values
pred_outcomes_05 = ifelse(probs.lda[,1] > probs.lda[,2], "B", "M")
pred_outcomes_25 = ifelse(probs.lda[,1] > 0.25, "B", "M")
pred_outcomes_75 = ifelse(probs.lda[,1] > 0.75, "B", "M")



d.Plot the ROC curve, computed on the test set.

e.Compute an estimate of the Area under the ROC Curve
(AUC).
The AUC score is 0.9396
3.Quadratic Discriminant Analysis Model.
a.Now fit a linear discriminant analysis model to the
training set you created in Exercise 1. Make a table displaying the
estimated ‘Prior probabilities of groups’ and ‘Group means’. Describe in
words the meaning of these estimates and how they are related to the
posterior probabilities.
The estimated group means are the average of the predictor variables
(radius or texture) for each group (malignant or benign). These
estimates are used to calculate the posterior probabilities which is the
probability of an observation belonging to malignant or benign given its
estimated predictor values. This calculation is based on Bayes’ theorem,
which combines prior knowledge (prior probabilities) and new information
(estimated predictor values) to estimate the probability of an
event.
## a.
## quadratic discriminant analysis
qda.wdbc = qda(Dig ~ V3 + V4, data = train_wdbc, center = TRUE)
qda.wdbc
qda_table <- cbind(qda.wdbc$prior, qda.wdbc$mean)
colnames(qda_table) <- c("Prior prob of groups", "Group means(radius)", "Group means(texture)")
kable(qda_table)
| 0 |
0.62 |
12.15901 |
17.87661 |
| 1 |
0.38 |
17.46270 |
21.68230 |
b.Use the fitted model and Bayes rule to compute the
predicted outcome Yˆ from the predicted posterior probabilities, both on
the training and test set. Then, compute the confusion table and
prediction accuracy both on the training and test set. Comment on the
results.
The two prediction accuracy are similar, which indicates that the
model fits well. The values are similar to the ones from LDA. It seems
that using LDA or QDA is not so much different.
##------------------
## b.
## fit the model and calculate post prob
train_pred.qda <- predict(qda.wdbc, X_train)$posterior
test_pred.qda <- predict(qda.wdbc, X_test)$posterior
## compute outcome Y based on the post prob on training set
train_outcome.qda <- apply(train_pred.qda, 1, which.max)
## outcome variable = 1 or 2
## transfer them to B or M
train_outcome.qda <- data.frame(train_outcome.qda)
train_outcome.qda$train_outcome.qda <-
ifelse(train_outcome.qda$train_outcome.qda==1, "B", "M")
## compute outcome Y based on the post prob on testing set
test_outcome.qda <- apply(test_pred.qda, 1, which.max)
## outcome variable = 1 or 2
## transfer them to B or M
test_outcome.qda <- data.frame(test_outcome.qda)
test_outcome.qda$test_outcome.qda <-
ifelse(test_outcome.qda$test_outcome.qda==1, "B", "M")
## confusion matrix
train_confusion.qda <- table(Prediction = train_outcome.qda$train_outcome.qda,
Actual = train_wdbc$V2)
test_confusion.qda <- table(Prediction = test_outcome.qda$test_outcome.qda,
Actual = test_wdbc$V2)
## prediction accuracy
train_accuracy.qda <- sum(diag(train_confusion.qda)) / nrow(train_wdbc)
test_accuracy.qda <- sum(diag(test_confusion.qda)) / nrow(test_wdbc)
kable(train_confusion.qda,
caption = "Confusion table for training set - QDA")
Confusion table for training set - QDA
| B |
236 |
34 |
| M |
12 |
118 |
kable(test_confusion.qda,
caption = "Confusion table for testing set - QDA")
Confusion table for testing set - QDA
| B |
101 |
14 |
| M |
8 |
46 |
c.(c) Plot an image of the QDA decision boundary (following
the steps in 1(g)). Generate the same plot for cutoff values of 0.25 and
0.75. Comment on the results.
The increase in the red region (predicted as benign tumor) as the
cutoff value decreases When the cutoff value is high, the model would be
more conservative in classifying an observation as benign, while as the
cutoff value decrease, the model becomes more confident in classifying
an observation as benign.
##------------------
## c.
## use the fitted GLM model to predict the outcome probabilities for each observation in the dense set
probs.qda <- predict(qda.wdbc, newdata = dense_set, type = "response")$posterior
# Compute the predicted outcomes using Bayes rule with different probability cutoff values
pred_outcomes_05.qda = ifelse(probs.qda[,1] > probs.qda[,2], "B", "M")
pred_outcomes_25.qda = ifelse(probs.qda[,1] > 0.25, "B", "M")
pred_outcomes_75.qda = ifelse(probs.qda[,1] > 0.75, "B", "M")



d.Plot the ROC curve, computed on the test set.

e.Compute an estimate of the Area under the ROC Curve
(AUC).
The AUC score is 0.9361
4.KNN Classifier.
a.For all choices of k = {1, 2, 3, 4, 20} (number of
neighbors), compute the predicted outcome Yˆ for each observation in the
training and test set. Then, compute the confusion table and prediction
accuracy both on the training and test set. Comment on the
results.
##------------------------------------ KNN
## a.
## knn
k_values <- c(1, 2, 3, 4, 20)
for (k in k_values) {
# train set
knn.train.label <- knn(train = train_wdbc[,c("V3", "V4")],
test = train_wdbc[,c("V3", "V4")],
cl = train_wdbc$V2, k = k)
# confusion table
confusion_table_train <- table(knn.train.label, train_wdbc$V2)
# prediction accuracy
accuracy_train <- sum(diag(confusion_table_train)) / sum(confusion_table_train)
# test set
knn.test.label <- knn(train = train_wdbc[,c("V3", "V4")],
test = test_wdbc[,c("V3", "V4")],
cl = train_wdbc$V2, k = k)
# confusion table - test set
confusion_table_test <- table(knn.test.label, test_wdbc$V2)
# prediction accuracy
accuracy_test <- sum(diag(confusion_table_test)) / sum(confusion_table_test)
# Print the results
cat("For k =", k, ":\n")
cat("Training set confusion table:\n")
print(confusion_table_train)
cat("Training set accuracy:", accuracy_train, "\n")
cat("Test set confusion table:\n")
print(confusion_table_test)
cat("Test set accuracy:", accuracy_test, "\n\n")
}
b.Plot an image of the decision boundary (following the steps
in 1(g)), for k = {1, 2, 3, 4, 20} (number of neighbors). Comment on the
results.
Based on the KNN decision boundary plots with different values of k,
we can see that the boundaries are less specific than the boundaries
using logistic regression, LDA, and QDA. It makes sense because KNN
models make predictions based on the classes of the nearest neighbors,
whereas logistic regression, LDA, and QDA are based on a more
sophisticated mathematical model. In addition, according to the five
plots with different values of k, we know that the decision boundary is
more precise when k=3. It seems that k=3 might be more appropriate in
this scenario.





c.Compute and plot the prediction accuracy (on both the
training and test set), for k = {1,2,…,19,20} (number of neighbors).
Which value of k would you choose? Comment on the results.
I would choose k=3. The followings are my reasons. Based on the plot
of prediction accuracy for different value of k on the training set, we
can see that the accuracy decrease as the k value increases. With the
elbow method, the elbow point is at k=2 or k=3. In addition, based on
the prediction accuracy for different values of k on the testing, we can
see that the accuracy at k=3, k=6, k=17, k=18, k=19, and k=20 are
relatively high. Therefore, I would choose k=3 as the best k value.
##------------------
## c.
## plot the prediction accuracy
train_accuracy = numeric(20)
test_accuracy = numeric(20)
for (i in 1:20) {
knn.label <- knn(train = train_wdbc[,c("V3","V4")],
test = train_wdbc[,c("V3","V4")],
cl = train_wdbc$V2, k = i)
train_accuracy[i] <- mean(knn.label == train_wdbc$V2)
test_accuracy[i] <- mean(knn.label == test_wdbc$V2)
}
for (i in 1:20) {
knn.label <- knn(train = train_wdbc[,c("V3", "V4")],
test = test_wdbc[,c("V3", "V4")],
cl = train_wdbc$V2, k = i)
test_accuracy[i] <- mean(knn.label == test_wdbc$V2)
}


LS0tCnRpdGxlOiAiQmluYXJ5IENsYXNzaWZpY2F0aW9uIG9uIHRoZSBCcmVhc3QgQ2FuY2VyIERpYWdub3N0aWMgRGF0YSIKc3VidGl0bGU6ICJTaW1wbGUgTG9naXN0aWMgUmVncmVzc2lvbiwgTGluZWFyL1F1YWRyYXRpYyBkaXNjcmltaW5hbnQgQW5hbHlzaXMgTW9kZWxzICYgS05OIgphdXRob3I6ICJNYWtheWxhIFRhbmciCmRhdGU6ICcyMDIzIEZlYicKYWJzdHJhY3Q6IAogIEluIHRoaXMgYW5hbHlzaXMsIHdlIHBlcmZvcm0gYmluYXJ5IGNsYXNzaWZpY2F0aW9uIG9uIHRoZSBCcmVhc3QgQ2FuY2VyIFdpc2NvbnNpbiAoRGlhZ25vc3RpYykgRGF0YSBTZXQgaW4gdGhlIGNzdiBmaWxlIHdkYmMuZGF0YS4gVGhlIGRhdGFzZXQgZGVzY3JpYmVzIGNoYXJhY3RlcmlzdGljcyBvZiB0aGUgY2VsbCBudWNsZWkgcHJlc2VudCBpbiBuIChzYW1wbGUgc2l6ZSkgaW1hZ2VzLiBFYWNoIGltYWdlIGhhcyBtdWx0aXBsZSBhdHRyaWJ1dGVzLCB3aGljaCBhcmUgZGVzY3JpYmVkIGluIGRldGFpbCBpbiB3ZGJjLm5hbWVzLiBXZSBwcmVkaWN0IHRoZSBhdHRyaWJ1dGUgaW4gY29sdW1uIDIsIHdoaWNoIHdlIGRlbm90ZSBieSBZLCByZXByZXNlbnRpbmcgdGhlIGRpYWdub3NpcyAoTSA9IG1hbGlnbmFudCwgQiA9IGJlbmlnbikuIEhlcmUsIHdlIGZvY3VzIG9uIHRoZSBhdHRyaWJ1dGVzIGluIGNvbHVtbnMgezI6RGlhZ25vc2lzLCAzOkF2ZXJhZ2UgcmFkaXVzIG9mIHRoZSBjZWxsIG51Y2xlaSBpbiBlYWNoIGltYWdlLCA0OkF2ZXJhZ2UgdGV4dHVyZSBvZiB0aGUgY2VsbCBudWNsZWkgaW4gZWFjaCBpbWFnZX0uIAogIFNwZWNpZmljYWxseSwgb3VyIGFpbSB3aWxsIGJlIHByZWRpY3RpbmcgYSBjYXRlZ29yaWNhbCB2YXJpYWJsZSBZIChEaWFnbm9zaXMg4oCTIGNvbHVtbiAyKSwgZnJvbSB0aGUgcXVhbnRpdGF0aXZlIGF0dHJpYnV0ZXMgWDEgKEF2ZXJhZ2UgcmFkaXVzIOKAkyBjb2x1bW4gMykgYW5kIFgyIChBdmVyYWdlIHRleHR1cmUg4oCTIGNvbHVtbiA0KS4gCm91dHB1dDogCiAgaHRtbF9kb2N1bWVudDoKICAgY29kZV9kb3dubG9hZDogdHJ1ZQogICAgICAgIAotLS0KCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUpCmtuaXRyOjpvcHRzX2NodW5rJHNldCh3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSkgCmBgYAoKYGBge3IgbGlicmFyeSwgaW5jbHVkZT1GQUxTRX0KIyMgbG9hZCB0aGUgbGlicmFyaWVzCmxpYnJhcnkoa25pdHIpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShNQVNTKSAgIyBJbXBsZW1lbnRzIExEQSAmIFFEQQpsaWJyYXJ5KGNhcmV0KSAjIGNvbmZ1c2lvbk1hdHJpeApsaWJyYXJ5KHBST0MpICAjIFJPQyAmIEFVQwpsaWJyYXJ5KGNsYXNzKSAjIEltcGxlbWVudHMgS05OCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkoR0dhbGx5KQoKIyMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyMgbG9hZCB0aGUgZGF0YXNldAp3ZGJjIDwtIHJlYWQuY3N2KCJ3ZGJjLmRhdGEiLCBoZWFkZXIgPSBGQUxTRSkKaGVhZCh3ZGJjKQojIyBzZWxlY3QgY29sdW1uIHsyLDMsNH0Kd2RiYyA8LSB3ZGJjICU+JSBzZWxlY3QoYygiVjIiLCAiVjMiLCAiVjQiKSkKCmBgYAoKCiMjIDEuRGF0YSBleHBsb3JhdGlvbiArIExvZ2lzdGljIFJlZ3Jlc3Npb24KCioqYS5EZXNjcmliZSB0aGUgZGF0YTogc2FtcGxlIHNpemUgbiwgbnVtYmVyIG9mIHByZWRpY3RvcnMgcCwgYW5kIG51bWJlciBvZiBvYnNlcnZhdGlvbnMgaW4gZWFjaCBjbGFzcy4qKgoKKiBuKHNhbXBsZSBzaXplKTogNTY5CiogbnVtYmVyIG9mIHByZWRpY3RvcnM6IDIKKiBudW1iZXIgb2Ygb2JzZXJ2YXRpb25zIGluIGVhY2ggY2xhc3M6IE09MjEyOyBCPTM1NyAKCmBgYHtyIDFhICxyZXN1bHRzPSdoaWRlJ30KIyMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0gCiMgYS4gCndkYmMgJT4lIHN1bW1hcmlzZShuPW4oKSkKd2RiYyAlPiUgZ3JvdXBfYnkoVjIpICU+JSBzdW1tYXJpc2Uobj1uKCksICkKZ2xpbXBzZSh3ZGJjKQpgYGAKCioqYi5EaXZpZGUgdGhlIGRhdGEgaW50byBhIHRyYWluaW5nIHNldCBvZiA0MDAgb2JzZXJ2YXRpb25zLCBhbmQgYSB0ZXN0IHNldDsgZnJvbSBub3cgb24sIHVubGVzcyBzcGVjaWZpZWQsIHdvcmsgb25seSBvbiB0aGUgdHJhaW5pbmcgc2V0LioqCgpgYGB7ciAxYiwgcmVzdWx0cyA9ICJoaWRlIn0KIyMtLS0tLS0tLS0tLS0tLS0tLS0KIyMgYi4KIyMgRGl2aWRlIHRoZSBkYXRhIGludG8gYSB0cmFpbmluZyBzZXQgb2YgNDAwIG9icwpzZXQuc2VlZCgxMDEpCnRyYWluX2lkX3dkYmMgPSBzYW1wbGUobnJvdyh3ZGJjKSwgNDAwKQp0cmFpbl93ZGJjID0gd2RiY1t0cmFpbl9pZF93ZGJjLCBdCnRlc3Rfd2RiYyAgPSB3ZGJjWy10cmFpbl9pZF93ZGJjLCBdCmBgYAoKKipjLk1ha2UgYSBzY2F0dGVycGxvdCBkaXNwbGF5aW5nIFkgKGNvbG9yIG9yIHNoYXBlIGVuY29kZWQpIGFuZCB0aGUgcHJlZGljLSB0b3JzIFgxLCBYMiAob24gdGhlIHgtIGFuZCB5LWF4aXMpLiBCYXNlZCBvbiB0aGlzIHNjYXR0ZXJwbG90LCBkbyB5b3UgdGhpbmsgaXQgd2lsbCBiZSBwb3NzaWJsZSB0byBhY2N1cmF0ZWx5IHByZWRpY3QgdGhlIG91dGNvbWUgZnJvbSB0aGUgcHJlZGljdG9ycz8gTW90aXZhdGUgeW91ciBhbnN3ZXIuKioKCkJhc2VkIG9uIHRoZSBwbG90LCB3ZSBjb25jbHVkZSB0aGF0IHRoZSBhdmVyYWdlIHJhZGl1cyBpcyBhIGJldHRlciBwcmVkaWN0b3IgZm9yIHRoZSBkaWFnbm9zaXMgYXMgdGhlIGJsdWUgcG9pbnRzIChkaWFnbm9zaXMgd2l0aCBtYWxpZ25hbnQpIHRlbmQgdG8gaGF2ZSBoaWdoZXIgdmFsdWVzIGFuZCB0aGUgcmVkIHBvaW50cyAoZGlhZ25vc2lzIHdpdGggYmVuaWduKSB0ZW5kIHRvIGhhdmUgbG93ZXIgdmFsdWVzLCB3aGlsZSB0aGUgYXZlcmFnZSB0ZXh0dXJlIGRvZXMgbm90IHNlZW0gdG8gYmUgYSBzdHJvbmcgcHJlZGljdG9yIGFzIHRoZSByZWQgcG9pbnRzIGFuZCB0aGUgYmx1ZSBwb2ludHMgYXJlIHNwcmVhZCBldmVubHkuCgpgYGB7ciAxYywgZWNobz1GQUxTRSwgb3V0LndpZHRoID0gIjcwJSIsIGZpZy5hbGlnbiA9ICdjZW50ZXInfQojIy0tLS0tLS0tLS0tLS0tLS0tLQojIyBjLgojIyBzY2F0dGVycGxvdAp0cmFpbl93ZGJjICU+JSBnZ3Bsb3QoYWVzKHggPSBWMywgeSA9IFY0LCBjb2xvciA9IGZhY3RvcihWMikpKSArIAogICAgICAgIGdlb21fcG9pbnQoKSArIAogICAgICAgIHhsYWIoIkF2ZXJhZ2UgcmFkaXVzIG9mIHRoZSBjZWxsIG51Y2xlaSIpICsgeWxhYigiQXZlcmFnZSB0ZXh0dXJlIG9mIHRoZSBjZWxsIG51Y2xlaSIpICsgCiAgICAgICAgZ2d0aXRsZSgiU2NhdHRlcnBsb3Qgb2YgcmFkaXVzIGFuZCB0ZXh1cmUgd2l0aCBjb2xvciBlbmNvZGVkIGRpYWdub3NpcyIpCgpgYGAKCioqZC5GaXQgYSBsb2dpc3RpYyByZWdyZXNzaW9uIG1vZGVsIHRvIHByZWRpY3QgWSBhbmQgbWFrZSBhIHRhYmxlLCBkaXNwbGF5aW5nIHRoZSBjb2VmZmljaWVudCBlc3RpbWF0ZXMsIHN0YW5kYXJkIGVycm9ycywgYW5kIHAtdmFsdWVzICh1c2UgY29tbWFuZCBzdW1tYXJ5KS4gR2l2ZSBhbiBpbnRlcnByZXRhdGlvbiBvZiB0aGUgdmFsdWVzIG9mIHRoZSBjb2VmZmljaWVudCBlc3RpbWF0ZXMuKioKCiogVGhlIGVzdGltYXRlZCBleHBlY3RlZCBsb2ctb2RkcyBvZiBkaWFnbm9zaXMgd2l0aCBtYWxpZ25hbnQgZm9yIGEgcG9wdWxhdGlvbiB3aXRoIGF2ZXJhZ2UgcmFkaXVtID0gMCBhbmQgYXZlcmFnZSB0ZXh0dXJlID0gMCBpcyAtMjEuMzgxCgoqIFRoZSBsb2ctb2RkcyBvZiBkaWFnbm9zaXMgd2l0aCBtYWxpZ25hbnQgaXMgZXN0aW1hdGVkIHRvIGJlIDEuMTIzIHRpbWVzIGdyZWF0ZXIgZm9yIGEgcG9wdWxhdGlvbiB3aXRoIG9uZSBtb3JlIHVuaXQgaW5jcmVhc2UgaW4gdGhlIGF2ZXJhZ2UgcmFkaXVzIG9mIHRoZSBjZWxsIG51Y2xlaSwgY29tcGFyZWQgdG8gYSBwb3B1bGF0aW9uIG9mIHRoZSBzYW1lIGF2ZXJhZ2UgdGV4dHVyZSBvZiB0aGUgY2VsbCBudWNsZWkuIAoKKiBUaGUgbG9nLW9kZHMgb2YgdGhlIGRpYWdub3NpcyB3aXRoIG1hbGlnbmFudCBpcyBlc3RpbWF0ZWQgdG8gYmUgMC4yNDcgdGltZXMgZ3JlYXRlciBmb3IgYSBwb3B1bGF0aW9uIHdpdGggb25lIG1vcmUgdW5pdCBpbmNyZWFzZSBpbiB0aGUgYXZlcmFnZSB0ZXh0dXJlIG9mIHRoZSBjZWxsIG51Y2xlaSwgY29tcGFyZWQgdG8gYSBwb3B1bGF0aW9uIG9mIHRoZSBzYW1lIGF2ZXJhZ2UgcmFkaXVzIG9mIHRoZSBjZWxsIG51Y2xlaS4KCmBgYHtyIDFkLCByZXN1bHRzPSdoaWRlJ30KIyMtLS0tLS0tLS0tLS0tLS0tLS0KIyMgbG9naXN0aWMgbW9kZWwKIyMgdHJhbnNmZXIgTSB2YXJpYWJsZSBmcm9tIGNoYXJhY3RlciB0byBudW1lcmljICgwLDEpCnRyYWluX3dkYmMkRGlnIDwtIGlmZWxzZSAodHJhaW5fd2RiYyRWMj09Ik0iLCAxLCAwKQoKZ2xtLndkYmMgPSBnbG0oRGlnIH4gVjMgKyBWNCwgZmFtaWx5ID0gYmlub21pYWwobGluayA9ICJsb2dpdCIpLCBkYXRhID0gdHJhaW5fd2RiYykKc3VtbWFyeShnbG0ud2RiYykKCmBgYAoKYGBge3IgMWQgdGFibGV9CmthYmxlKHN1bW1hcnkoZ2xtLndkYmMpJGNvZWZmaWNpZW50cykKYGBgCgoqKmUuVXNlIHRoZSBjb2VmZmljaWVudCBlc3RpbWF0ZXMgdG8gbWFudWFsbHkgY2FsY3VsYXRlIHRoZSBwcmVkaWN0ZWQgcHJvYmFiaWwtIGl0eSBQKFkgPSBNIHwgKFgxLFgyKSA9ICgxMCwxMikpIHdyaXRpbmcgZXhwbGljaXRseSBldmVyeSBzdGVwLiBDb21wYXJlIHlvdXIgcmVzdWx0cyB3aXRoIHRoZSBwcmVkaWN0aW9uIGNvbXB1dGVkIHdpdGggcHJlZGljdC4qKgoKKiBtYW51YWxseSBjYWxjdWxhdGU6ICRwcmVkaWN0aW9uID0gLTIxLjM4MSArIDEuMTIzKjEwICsgMC4yNDcqMTIgPSAtNy4xNzg5MDEkLCAkcHJvYmFiaWxpdHkgPSAxLygxK2V4cCgtNy4xNzg5MDEpID0gMC4wMDA3NiQKCiogcHJlZGljdChnbG0ud2RiYywgZGF0YS5mcmFtZShWMyA9IDEwLCBWNCA9IDEyKSwgdHlwZSA9ICJyZXNwb25zZSIpID0gMC4wMDA3NgoKKiBUaGVyZWZvcmUsIHRoZSB0d28gcmVzdWx0cyBhcmUgdGhlIHNhbWUuIAoKYGBge3IgMWUsIHJlc3VsdHM9ImhpZGUifQojIy0tLS0tLS0tLS0tLS0tLS0tLQojIyBlLgojIyBleHRyYWN0IHRoZSBjb2VmZmljaWVudHMKYmV0YV8wIDwtIGNvZWYoZ2xtLndkYmMpWzFdCmJldGFfMSA8LSBjb2VmKGdsbS53ZGJjKVsyXQpiZXRhXzIgPC0gY29lZihnbG0ud2RiYylbM10KCiMjIGNhbGN1bGF0ZSB0aGUgbGluZWFyIHByZWRpY3RvcgpwcmVkIDwtIGJldGFfMCArIGJldGFfMSoxMCArIGJldGFfMioxMgoKIyMgY2FsY3VsYXRlIHRoZSBwcmVkaWN0ZWQgcHJvYmFiaWxpdHkKcHJvYiA8LSAxIC8gKDEgKyBleHAoLXByZWQpKQpwcm9iCiMgMC4wMDA3NjE5MjQ2CgojIGNvbXBhcmUgd2l0aCB0aGUgcHJlZGljdGlvbiBjb21wdXRlZCB1c2luZyBwcmVkaWN0CnByZWRpY3QoZ2xtLndkYmMsIGRhdGEuZnJhbWUoVjMgPSAxMCwgVjQgPSAxMiksIHR5cGUgPSAicmVzcG9uc2UiKQojIDAuMDAwNzYxOTI0NgoKIyBUaGUgdHdvIHZhbHVlcyBhcmUgdGhlIHNhbWUuIApgYGAKCioqZi5Vc2UgdGhlIGdsbSBwcmV2aW91c2x5IGZpdHRlZCBhbmQgdGhlIEJheWVzIHJ1bGUgdG8gY29tcHV0ZSB0aGUgcHJlLSBkaWN0ZWQgb3V0Y29tZSBZy4YgZnJvbSB0aGUgYXNzb2NpYXRlZCBwcm9iYWJpbGl0eSBlc3RpbWF0ZXMgKGNvbXB1dGVkIHdpdGggcHJlZGljdCkgYm90aCBvbiB0aGUgdHJhaW5pbmcgYW5kIHRoZSB0ZXN0IHNldC4gVGhlbiBjb21wdXRlIHRoZSBjb25mdXNpb24gdGFibGUgYW5kIHByZWRpY3Rpb24gYWNjdXJhY3kgKHJhdGUgb2YgY29ycmVjdGx5IGNsYXNzaWZpZWQgb2JzZXItIHZhdGlvbnMpIGJvdGggb24gdGhlIHRyYWluaW5nIGFuZCB0ZXN0IHNldC4gQ29tbWVudCBvbiB0aGUgcmVzdWx0cy4qKgoKKiB0cmFpbmluZyBzZXQ6IGFjY3VyYWN5ID0gMC45MDUKCiogdGVzdGluZyBzZXQ6IGFjY3VyYWN5ID0gMC44ODIKClRoZSBtb2RlbCBhY2N1cmFjeSBmb3IgdHJhaW5pbmcgZGF0YSBhbmQgdGhlIG9uZSBmb3IgdGVzdGluZyBkYXRhIGFyZSBzaW1pbGFyLiBJdCBpbmRpY2F0ZXMgdGhhdCB0aGUgZ29vZCBtb2RlbCBwZXJmb3JtYW5jZS4gCgpgYGB7ciAxZiwgcmVzdWx0cz0naGlkZSd9CiMjLS0tLS0tLS0tLS0tLS0tLS0tCiMjIGYuCiMjIENvbXB1dGUgdGhlIHByZWRpY3RlZCBwcm9iYWJpbGl0aWVzIHVzaW5nIHRoZSBwcmVkaWN0IGZ1bmN0aW9uCnRyYWluX3Byb2JzIDwtIHByZWRpY3QoZ2xtLndkYmMsIG5ld2RhdGEgPSB0cmFpbl93ZGJjLCB0eXBlID0gInJlc3BvbnNlIikKdGVzdF9wcm9icyA8LSBwcmVkaWN0KGdsbS53ZGJjLCBuZXdkYXRhID0gdGVzdF93ZGJjLCB0eXBlID0gInJlc3BvbnNlIikKCiMjIFVzZSB0aGUgQmF5ZXMgcnVsZSB0byBjb21wdXRlIHRoZSBwcmVkaWN0ZWQgb3V0Y29tZSAKdHJhaW5fcHJlZGljdGlvbiA8LSBpZmVsc2UodHJhaW5fcHJvYnMgPiAwLjUsICJNIiwgIkIiKQp0ZXN0X3ByZWRpY3Rpb24gPC0gaWZlbHNlKHRlc3RfcHJvYnMgPiAwLjUsICJNIiwgIkIiKQoKIyMgQ3JlYXRlIGNvbmZ1c2lvbiB0YWJsZXMgb24gdGhlIHRyYWluaW5nIGFuZCB0ZXN0IHNldHMKdHJhaW5fY29uZnVzaW9uX3RhYmxlIDwtIHRhYmxlKFByZWRpY3Rpb24gPSB0cmFpbl9wcmVkaWN0aW9uLCBBY3R1YWwgPSB0cmFpbl93ZGJjJFYyKQp0ZXN0X2NvbmZ1c2lvbl90YWJsZSA8LSB0YWJsZShQcmVkaWN0aW9uID0gdGVzdF9wcmVkaWN0aW9uLCBBY3R1YWwgPSB0ZXN0X3dkYmMkVjIpCgojIyBDYWxjdWxhdGUgcHJlZGljdGlvbiBhY2N1cmFjeSBvbiB0aGUgdHJhaW5pbmcgYW5kIHRlc3Qgc2V0cwp0cmFpbl9hY2N1cmFjeSA8LSBzdW0oZGlhZyh0cmFpbl9jb25mdXNpb25fdGFibGUpKSAvIG5yb3codHJhaW5fd2RiYykKdGVzdF9hY2N1cmFjeSA8LSBzdW0oZGlhZyh0ZXN0X2NvbmZ1c2lvbl90YWJsZSkpIC8gbnJvdyh0ZXN0X3dkYmMpCgpgYGAKCmBgYHtyIDFmIHRhYmxlfQprYWJsZSh0cmFpbl9jb25mdXNpb25fdGFibGUsCiAgICAgIGNhcHRpb24gPSAiQ29uZnVzaW9uIFRhYmxlIGZvciB0cmFpbmluZyBzZXQiKQpgYGAKCmBgYHtyIDJmIHRhYmxlfQprYWJsZSh0ZXN0X2NvbmZ1c2lvbl90YWJsZSwgCiAgICAgIGNhcHRpb24gPSAiQ29uZnVzaW9uIFRhYmxlIGZvciB0ZXN0aW5nIHNldCIpCmBgYAoKKipnLlBsb3QgYW4gaW1hZ2Ugb2YgdGhlIGRlY2lzaW9uIGJvdW5kYXJ5IGFzIGZvbGxvd3M6MS5HZW5lcmF0ZSBhIGRlbnNlIHNldCAoZS5nLiAxMDAwMCBvYnNlcnZhdGlvbnMpIG9mIHBvc3NpYmxlIHZhbHVlcyBmb3IgdGhlIHByZWRpY3RvcnMgKFgxLFgyKSB3aXRoaW4gcmVhc29uYWJsZSByYW5nZXM7ICh0aGUgY29tbWFuZCBleHBhbmQuZ3JpZCBtaWdodCBjb21lIGluIGhhbmR5KTsgMi5Vc2UgdGhlIGdsbSBtb2RlbCBwcmV2aW91c2x5IGZpdHRlZCB0byBwcmVkaWN0IHRoZSBvdXRjb21lIHByb2JhYmlsLSBpdGllcyBmb3IgZXZlcnkgb2JzZXJ2YXRpb24geW91IGhhdmUgZ2VuZXJhdGVkIGFuZCB1c2UgQmF5ZXMgcnVsZSB0byBjb21wdXRlIHRoZSBwcmVkaWN0ZWQgb3V0Y29tZXM7IDMuUGxvdCBwcmVkaWN0ZWQgb3V0Y29tZXMgKGNvbG9yIGNvZGVkKSBhbmQgYXNzb2NpYXRlZCBwcmVkaWN0b3JzIGluIGEgMkQgc2NhdHRlciBwbG90IHRvZ2V0aGVyIHdpdGggdGhlIHRyYWluaW5nIHNldC5HZW5lcmF0ZSB0aGUgc2FtZSBwbG90IGZvciBwcm9iYWJpbGl0eSBjdXRvZmYgdmFsdWVzIG9mIDAuMjUgYW5kIDAuNzUuIENvbW1lbnQgb24gdGhlIHJlc3VsdHMuKioKClRoZSBpbmNyZWFzZSBpbiB0aGUgcmVkIHJlZ2lvbiAocHJlZGljdGVkIGFzIGJlbmlnbiB0dW1vcikgYXMgdGhlIGN1dG9mZiB2YWx1ZSBpbmNyZWFzZXMuIFdoZW4gdGhlIGN1dG9mZiB2YWx1ZSBpcyBsb3csIHRoZSBtb2RlbCB3b3VsZCBiZSBtb3JlIGNvbnNlcnZhdGl2ZSBpbiBjbGFzc2lmeWluZyBhbiBvYnNlcnZhdGlvbiBhcyBiZW5pZ24sIHdoaWxlIGFzIHRoZSBjdXRvZmYgdmFsdWUgaW5jcmVhc2VzLCB0aGUgbW9kZWwgYmVjb21lcyBtb3JlIGNvbmZpZGVudCBpbiBjbGFzc2lmeWluZyBhbiBvYnNlcnZhdGlvbiBhcyBiZW5pZ24uIFRoZXJlZm9yZSwgdGhlIHJlZCByZWdpb24gd291bGQgaW5jcmVhc2Ugd2l0aCBoaWdoZXIgY3V0b2ZmIHZhbHVlLiAKCmBgYHtyIDFnLCByZXN1bHRzPSdoaWRlJ30KIyMgZy4KIyMgc3BlY2lmeSByZWFzb25hYmxlIHJhbmdlcyBmb3IgWDEgYW5kIFgyCngxX3JhbmdlIDwtIGMobWluKHRyYWluX3dkYmMkVjMpLCBtYXgodHJhaW5fd2RiYyRWMykpCngyX3JhbmdlIDwtIGMobWluKHRyYWluX3dkYmMkVjQpLCBtYXgodHJhaW5fd2RiYyRWNCkpCgojIyBjcmVhdGUgYSBkZW5zZSBzZXQgb2YgcG9zc2libGUgdmFsdWVzIGZvciBYMSBhbmQgWDIgdXNpbmcgZXhwYW5kLmdyaWQKZGVuc2Vfc2V0IDwtIGV4cGFuZC5ncmlkKFYzID0gc2VxKGZyb20gPSB4MV9yYW5nZVsxXSwgdG8gPSB4MV9yYW5nZVsyXSwgbGVuZ3RoLm91dCA9IDEwMCksCiAgICAgICAgICAgICAgICAgICAgICAgICBWNCA9IHNlcShmcm9tID0geDJfcmFuZ2VbMV0sIHRvID0geDJfcmFuZ2VbMl0sIGxlbmd0aC5vdXQgPSAxMDApKQoKIyMgdXNlIHRoZSBmaXR0ZWQgR0xNIG1vZGVsIHRvIHByZWRpY3QgdGhlIG91dGNvbWUgcHJvYmFiaWxpdGllcyBmb3IgZWFjaCBvYnNlcnZhdGlvbiBpbiB0aGUgZGVuc2Ugc2V0CmRlbnNlX3NldCRwcm9icyA8LSBwcmVkaWN0KGdsbS53ZGJjLCBuZXdkYXRhID0gZGVuc2Vfc2V0LCB0eXBlID0gInJlc3BvbnNlIikKCiMjIGNvbXB1dGUgdGhlIHByZWRpY3RlZCBvdXRjb21lcyB1c2luZyBCYXllcyBydWxlCmRlbnNlX3NldCRwcmVkaWN0aW9uIDwtIGlmZWxzZShkZW5zZV9zZXQkcHJvYnMgPiAwLjUsICJNIiwgIkIiKQpgYGAKCmBgYHtyIDFnX3Bsb3QxLCBlY2hvPUZBTFNFLCBvdXQud2lkdGggPSAiNzAlIiwgZmlnLmFsaWduID0gJ2NlbnRlcid9CiMjIFBsb3QgdGhlIHByZWRpY3RlZCBvdXRjb21lcyBmb3IgcHJvYmFiaWxpdHkgY3V0b2ZmIG9mIDAuNQpnZ3Bsb3QoZGF0YSA9IGRlbnNlX3NldCwgYWVzKHggPSBWMywgeSA9IFY0LCBjb2xvciA9IGZhY3RvcihwcmVkaWN0aW9uKSkpICsKICAgICAgICBnZW9tX3BvaW50KGFscGhhID0gMC4yKSArCiAgICAgICAgZ2VvbV9wb2ludChkYXRhID0gdHJhaW5fd2RiYywgYWVzKHggPSBWMywgeSA9IFY0LCBjb2xvciA9IGZhY3RvcihWMikpKSArCiAgICAgICAgc2NhbGVfY29sb3JfZGlzY3JldGUobmFtZSA9ICJQcmVkaWN0aW9uIikgKwogICAgICAgIGdndGl0bGUoIkxvZ2lzdGljIFJlZ3Jlc3Npb24gRGVjaXNpb24gQm91bmRhcnkgKEN1dG9mZiA9IDAuNSkiKSArCiAgICAgICAgeGxhYigiQXZlcmFnZSByYWRpdXMiKSArIHlsYWIoIkF2ZXJhZ2UgdGV4dHVyZSIpCmBgYAoKYGBge3IgMWdfcGxvdDIsIGVjaG89RkFMU0UsIG91dC53aWR0aCA9ICI3MCUiLCBmaWcuYWxpZ24gPSAnY2VudGVyJ30KZGVuc2Vfc2V0JHByZWRpY3Rpb25fMjUgPC0gaWZlbHNlKGRlbnNlX3NldCRwcm9icyA+IDAuMjUsICJNIiwgIkIiKQpnZ3Bsb3QoZGF0YSA9IGRlbnNlX3NldCwgYWVzKHggPSBWMywgeSA9IFY0LCBjb2xvciA9IGZhY3RvcihwcmVkaWN0aW9uXzI1KSkpICsKICAgICAgICBnZW9tX3BvaW50KGFscGhhID0gMC4yKSArCiAgICAgICAgZ2VvbV9wb2ludChkYXRhID0gdHJhaW5fd2RiYywgYWVzKHggPSBWMywgeSA9IFY0LCBjb2xvciA9IGZhY3RvcihWMikpKSArCiAgICAgICAgc2NhbGVfY29sb3JfZGlzY3JldGUobmFtZSA9ICJQcmVkaWN0aW9uIikgKwogICAgICAgIGdndGl0bGUoIkxvZ2lzdGljIFJlZ3Jlc3Npb24gRGVjaXNpb24gQm91bmRhcnkgKEN1dG9mZiA9IDAuMjUpIikgKwogICAgICAgIHhsYWIoIkF2ZXJhZ2UgcmFkaXVzIikgKyB5bGFiKCJBdmVyYWdlIHRleHR1cmUiKQpgYGAKCmBgYHtyIDFnX3Bsb3QzLCBlY2hvPUZBTFNFLCBvdXQud2lkdGggPSAiNzAlIiwgZmlnLmFsaWduID0gJ2NlbnRlcid9CmRlbnNlX3NldCRwcmVkaWN0aW9uXzc1IDwtIGlmZWxzZShkZW5zZV9zZXQkcHJvYnMgPiAwLjc1LCAiTSIsICJCIikKZ2dwbG90KGRhdGEgPSBkZW5zZV9zZXQsIGFlcyh4ID0gVjMsIHkgPSBWNCwgY29sb3IgPSBmYWN0b3IocHJlZGljdGlvbl83NSkpKSArCiAgICAgICAgZ2VvbV9wb2ludChhbHBoYSA9IDAuMikgKwogICAgICAgIGdlb21fcG9pbnQoZGF0YSA9IHRyYWluX3dkYmMsIGFlcyh4ID0gVjMsIHkgPSBWNCwgY29sb3IgPSBmYWN0b3IoVjIpKSkgKwogICAgICAgIHNjYWxlX2NvbG9yX2Rpc2NyZXRlKG5hbWUgPSAiUHJlZGljdGlvbiIpICsKICAgICAgICBnZ3RpdGxlKCJMb2dpc3RpYyBSZWdyZXNzaW9uIERlY2lzaW9uIEJvdW5kYXJ5IChDdXRvZmYgPSAwLjc1KSIpICsKICAgICAgICB4bGFiKCJBdmVyYWdlIHJhZGl1cyIpICsgeWxhYigiQXZlcmFnZSB0ZXh0dXJlIikKYGBgCgoqKmguUGxvdCB0aGUgUk9DIGN1cnZlLCBjb21wdXRlZCBvbiB0aGUgdGVzdCBzZXQuKioKCmBgYHtyIDFoLCBlY2hvID0gRkFMU0UsIG91dC53aWR0aCA9ICI2MCUiLCBmaWcuYWxpZ24gPSAnY2VudGVyJ30KIyMtLS0tLS0tLS0tLS0tLS0tLS0KIyMgaC4KdGVzdF9wcm9icyA8LSBwcmVkaWN0KGdsbS53ZGJjLCBuZXdkYXRhID0gdGVzdF93ZGJjLCB0eXBlID0gInJlc3BvbnNlIikKIyMgY2FsY3VsYXRlIEFVQyBzY29yZQpyb2Nfc2NvcmUgPSByb2MocmVzcG9uc2UgPSB0ZXN0X3dkYmMkVjIsIHByZWRpY3RvciA9IHRlc3RfcHJvYnMpICAKI3JvY19zY29yZQoKIyMgcGxvdCB0aGUgUk9DIGN1cnZlCmdncm9jKHJvY19zY29yZSwgbGluZXR5cGU9MSwgc2l6ZSA9IDEpICsgCiAgICAgICAgeGxhYigiRlBSIikgKyB5bGFiKCJUUFIiKSArCiAgICAgICAgZ2d0aXRsZSgiVGVzdCBST0MgY3VydmUgZm9yIHRoZSBMb2dpc3RpYyBSZWdyZXNzaW9uIE1vZGVsIikKCmBgYAoKKippLkNvbXB1dGUgYW4gZXN0aW1hdGUgb2YgdGhlIEFyZWEgdW5kZXIgdGhlIFJPQyBDdXJ2ZSAoQVVDKS4qKgoKVGhlIEFVQyBzY29yZSBpcyAwLjkzOTguCgojIyAyLkxpbmVhciBEaXNjcmltaW5hbnQgQW5hbHlzaXMgTW9kZWwKCioqYS5Ob3cgZml0IGEgbGluZWFyIGRpc2NyaW1pbmFudCBhbmFseXNpcyBtb2RlbCB0byB0aGUgdHJhaW5pbmcgc2V0IHlvdSBjcmVhdGVkIGluIEV4ZXJjaXNlIDEuIE1ha2UgYSB0YWJsZSBkaXNwbGF5aW5nIHRoZSBlc3RpbWF0ZWQg4oCYUHJpb3IgcHJvYmFiaWxpdGllcyBvZiBncm91cHPigJkgYW5kIOKAmEdyb3VwIG1lYW5z4oCZLiBEZXNjcmliZSBpbiB3b3JkcyB0aGUgbWVhbmluZyBvZiB0aGVzZSBlc3RpbWF0ZXMgYW5kIGhvdyB0aGV5IGFyZSByZWxhdGVkIHRvIHRoZSBwb3N0ZXJpb3IgcHJvYmFiaWxpdGllcy4qKgoKVGhlIGVzdGltYXRlZCBncm91cCBtZWFucyBhcmUgdGhlIGF2ZXJhZ2Ugb2YgdGhlIHByZWRpY3RvciB2YXJpYWJsZXMgKHJhZGl1cyBvciB0ZXh0dXJlKSBmb3IgZWFjaCBncm91cCAobWFsaWduYW50IG9yIGJlbmlnbikuIFRoZXNlIGVzdGltYXRlcyBhcmUgdXNlZCB0byBjYWxjdWxhdGUgdGhlIHBvc3RlcmlvciBwcm9iYWJpbGl0aWVzIHdoaWNoIGlzIHRoZSBwcm9iYWJpbGl0eSBvZiBhbiBvYnNlcnZhdGlvbiBiZWxvbmdpbmcgdG8gbWFsaWduYW50IG9yIGJlbmlnbiBnaXZlbiBpdHMgZXN0aW1hdGVkIHByZWRpY3RvciB2YWx1ZXMuIFRoaXMgY2FsY3VsYXRpb24gaXMgYmFzZWQgb24gQmF5ZXMnIHRoZW9yZW0sIHdoaWNoIGNvbWJpbmVzIHByaW9yIGtub3dsZWRnZSAocHJpb3IgcHJvYmFiaWxpdGllcykgYW5kIG5ldyBpbmZvcm1hdGlvbiAoZXN0aW1hdGVkIHByZWRpY3RvciB2YWx1ZXMpIHRvIGVzdGltYXRlIHRoZSBwcm9iYWJpbGl0eSBvZiBhbiBldmVudC4gCgpgYGB7ciAyYSwgcmVzdWx0cz0naGlkZSd9CiMjLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tIExEQQojIyBhLgojIyBsaW5lYXIgZGlzY3JpbWluYW50IG1vZGVsCmxkYS53ZGJjID0gbGRhKERpZyB+IFYzICsgVjQsIGRhdGEgPSB0cmFpbl93ZGJjLCBjZW50ZXIgPSBUUlVFKSAKCmxkYV90YWJsZSA8LSBjYmluZChsZGEud2RiYyRwcmlvciwgbGRhLndkYmMkbWVhbikKY29sbmFtZXMobGRhX3RhYmxlKSA8LSBjKCJQcmlvciBwcm9iIG9mIGdyb3VwcyIsICJHcm91cCBtZWFucyhyYWRpdXMpIiwgIkdyb3VwIG1lYW5zKHRleHR1cmUpIikKCmBgYAoKYGBge3IgMmF0YWJsZX0Ka2FibGUobGRhX3RhYmxlKQpgYGAKCioqYi5Vc2UgdGhlIGZpdHRlZCBtb2RlbCBhbmQgQmF5ZXMgcnVsZSB0byBjb21wdXRlIHRoZSBwcmVkaWN0ZWQgb3V0Y29tZSBZy4YgZnJvbSB0aGUgcHJlZGljdGVkIHBvc3RlcmlvciBwcm9iYWJpbGl0aWVzLCBib3RoIG9uIHRoZSB0cmFpbmluZyBhbmQgdGVzdCBzZXQuIFRoZW4sIGNvbXB1dGUgdGhlIGNvbmZ1c2lvbiB0YWJsZSBhbmQgcHJlZGljdGlvbiBhY2N1cmFjeSBib3RoIG9uIHRoZSB0cmFpbmluZyBhbmQgdGVzdCBzZXQuIENvbW1lbnQgb24gdGhlIHJlc3VsdHMuKioKCiogUHJlZGljdGlvbiBhY2N1cmFjeSBvbiB0cmFpbmluZyBzZXQgPSAwLjg5CgoqIFByZWRpY3Rpb24gYWNjdXJhY3kgb24gdGVzdGluZyBzZXQgPSAwLjg3NgoKVGhlIHR3byBwcmVkaWN0aW9uIGFjY3VyYWN5IGFyZSBzaW1pbGFyLCBpdCBpbmRpY2F0ZXMgdGhhdCB0aGUgbW9kZWwgaGFzIGEgZ29vZCBwZXJmb3JtYW5jZS4gVGhlIHZhbHVlcyBhcmUgc2xpZ2h0bHkgc21hbGxlciB0aGFuIHRoZSBhY2N1cmFjeSBmcm9tIGZpdHRpbmcgdGhlIGxvZ2lzdGljIG1vZGVsLiBMREEgZmluZHMgdGhlIG9wdGltYWwgbGluZWFyIGNvbWJpbmF0aW9ucyBvZiBwcmVkaWN0b3JzIHRoYXQgbWF4aW1pemUgdGhlIHNlcGFyYXRpb24gYmV0d2VlbiBjbGFzc2VzIGFuZCB0aGVuIHVzZXMgQmF5ZXMnIHRoZW9yZW0gdG8gbWFrZSBwcmVkaWN0aW9ucywgd2hpbGUgbG9naXN0aWMgcmVncmVzc2lvbiB1c2VzIG1heGltdW0gbGlrZWxpaG9vZCBlc3RpbWF0aW9uIHRvIGZpdCBhIGxvZ2lzdGljIGZ1bmN0aW9uIHRvIHRoZSBkYXRhLiBJbiB0aGlzIHNjZW5hcmlvLCBpdCBzZWVtcyB0aGF0IHRoZSBsb2dpc3RpYyBtb2RlbCBpcyBtb3JlIGFwcHJvcHJpYXRlIGJlY2F1c2UgTERBIHdpdGggbGluZWFyIGNvbWJpbmF0aW9uIG1pZ2h0IGJlIHJlbGF0aXZlIHNpbXBsZXIgZm9yIHRoZSBkYXRhLiAKCmBgYHtyIDJiLCByZXN1bHRzPSdoaWRlJ30KIyMtLS0tLS0tLS0tLS0tLS0tLS0KIyMgYgpYX3RyYWluIDwtIHRyYWluX3dkYmMgJT4lIHNlbGVjdChjKFYzLCBWNCkpClhfdGVzdCA8LSB0ZXN0X3dkYmMgJT4lIHNlbGVjdChjKFYzLCBWNCkpCgojIyBmaXQgdGhlIG1vZGVsIGFuZCBjYWxjdWxhdGUgcG9zdCBwcm9iCnRyYWluX3ByZWQubGRhIDwtIHByZWRpY3QobGRhLndkYmMsIFhfdHJhaW4pJHBvc3Rlcmlvcgp0ZXN0X3ByZWQubGRhIDwtIHByZWRpY3QobGRhLndkYmMsIFhfdGVzdCkkcG9zdGVyaW9yCgojIyBjb21wdXRlIG91dGNvbWUgWSBiYXNlZCBvbiB0aGUgcG9zdCBwcm9iCnRyYWluX291dGNvbWUubGRhIDwtIGFwcGx5KHRyYWluX3ByZWQubGRhLCAxLCB3aGljaC5tYXgpCiMjIG91dGNvbWUgdmFyaWFibGUgPSAxIG9yIDIgCiMjIHRyYW5zZmVyIHRoZW0gdG8gQiBvciBNCnRyYWluX291dGNvbWUubGRhIDwtIGRhdGEuZnJhbWUodHJhaW5fb3V0Y29tZS5sZGEpCnRyYWluX291dGNvbWUubGRhJHRyYWluX291dGNvbWUubGRhIDwtIAogICAgICAgIGlmZWxzZSh0cmFpbl9vdXRjb21lLmxkYSR0cmFpbl9vdXRjb21lLmxkYT09MSwgIkIiLCAiTSIpCgojIyBEbyBpdCBhZ2FpbiwgYnV0IHVzaW5nIHRlc3Rpbmcgc2V0CnRlc3Rfb3V0Y29tZS5sZGEgPC0gYXBwbHkodGVzdF9wcmVkLmxkYSwgMSwgd2hpY2gubWF4KQojIyBvdXRjb21lIHZhcmlhYmxlID0gMSBvciAyIAojIyB0cmFuc2ZlciB0aGVtIHRvIEIgb3IgTQp0ZXN0X291dGNvbWUubGRhIDwtIGRhdGEuZnJhbWUodGVzdF9vdXRjb21lLmxkYSkKdGVzdF9vdXRjb21lLmxkYSR0ZXN0X291dGNvbWUubGRhIDwtIAogICAgICAgIGlmZWxzZSh0ZXN0X291dGNvbWUubGRhJHRlc3Rfb3V0Y29tZS5sZGE9PTEsICJCIiwgIk0iKQoKIyMgY29uZnVzaW9uIG1hdHJpeAp0cmFpbl9jb25mdXNpb24ubGRhIDwtIHRhYmxlKFByZWRpY3Rpb24gPSB0cmFpbl9vdXRjb21lLmxkYSR0cmFpbl9vdXRjb21lLmxkYSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgQWN0dWFsID0gdHJhaW5fd2RiYyRWMikKdGVzdF9jb25mdXNpb24ubGRhIDwtIHRhYmxlKFByZWRpY3Rpb24gPSB0ZXN0X291dGNvbWUubGRhJHRlc3Rfb3V0Y29tZS5sZGEsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgQWN0dWFsID0gdGVzdF93ZGJjJFYyKQoKIyMgcHJlZGljdGlvbiBhY2N1cmFjeQp0cmFpbl9hY2N1cmFjeS5sZGEgPC0gc3VtKGRpYWcodHJhaW5fY29uZnVzaW9uLmxkYSkpIC8gbnJvdyh0cmFpbl93ZGJjKQp0ZXN0X2FjY3VyYWN5LmxkYSA8LSBzdW0oZGlhZyh0ZXN0X2NvbmZ1c2lvbi5sZGEpKSAvIG5yb3codGVzdF93ZGJjKQoKYGBgCgpgYGB7ciAyYiB0YWJsZTF9CmthYmxlKHRyYWluX2NvbmZ1c2lvbi5sZGEsCiAgICAgIGNhcHRpb24gPSAiQ29uZnVzaW9uIHRhYmxlIGZvciB0cmFpbmluZyBzZXQgLSBMREEiKQoKYGBgCgpgYGB7ciAyYiB0YWJsZTJ9CmthYmxlKHRlc3RfY29uZnVzaW9uLmxkYSwKICAgICAgY2FwdGlvbiA9ICJDb25mdXNpb24gdGFibGUgZm9yIHRlc3Rpbmcgc2V0IC0gTERBIikKCmBgYAoKKipjLlBsb3QgYW4gaW1hZ2Ugb2YgdGhlIExEQSBkZWNpc2lvbiBib3VuZGFyeSAoZm9sbG93aW5nIHRoZSBzdGVwcyBpbiAxKGcpKS4gR2VuZXJhdGUgdGhlIHNhbWUgcGxvdCBmb3IgY3V0b2ZmIHZhbHVlcyBvZiAwLjI1IGFuZCAwLjc1LiBDb21tZW50IG9uIHRoZSByZXN1bHRzLioqCgpUaGUgaW5jcmVhc2UgaW4gdGhlIHJlZCByZWdpb24gKHByZWRpY3RlZCBhcyBiZW5pZ24gdHVtb3IpIGFzIHRoZSBjdXRvZmYgdmFsdWUgZGVjcmVhc2VzIFdoZW4gdGhlIGN1dG9mZiB2YWx1ZSBpcyBoaWdoLCB0aGUgbW9kZWwgd291bGQgYmUgbW9yZSBjb25zZXJ2YXRpdmUgaW4gY2xhc3NpZnlpbmcgYW4gb2JzZXJ2YXRpb24gYXMgYmVuaWduLCB3aGlsZSBhcyB0aGUgY3V0b2ZmIHZhbHVlIGRlY3JlYXNlLCB0aGUgbW9kZWwgYmVjb21lcyBtb3JlIGNvbmZpZGVudCBpbiBjbGFzc2lmeWluZyBhbiBvYnNlcnZhdGlvbiBhcyBiZW5pZ24uIAoKYGBge3IgMmMsIHJlc3VsdHM9J2hpZGUnfQojIy0tLS0tLS0tLS0tLS0tLS0tLQojIyBjLgojIyB1c2UgdGhlIGZpdHRlZCBtb2RlbCB0byBwcmVkaWN0IHRoZSBvdXRjb21lIHByb2JhYmlsaXRpZXMgZm9yIGVhY2ggb2JzZXJ2YXRpb24gaW4gdGhlIGRlbnNlIHNldApwcm9icy5sZGEgPC0gcHJlZGljdChsZGEud2RiYywgbmV3ZGF0YSA9IGRlbnNlX3NldCwgdHlwZSA9ICJyZXNwb25zZSIpJHBvc3RlcmlvcgoKIyBDb21wdXRlIHRoZSBwcmVkaWN0ZWQgb3V0Y29tZXMgdXNpbmcgQmF5ZXMgcnVsZSB3aXRoIGRpZmZlcmVudCBwcm9iYWJpbGl0eSBjdXRvZmYgdmFsdWVzCnByZWRfb3V0Y29tZXNfMDUgPSBpZmVsc2UocHJvYnMubGRhWywxXSA+IHByb2JzLmxkYVssMl0sICJCIiwgIk0iKQpwcmVkX291dGNvbWVzXzI1ID0gaWZlbHNlKHByb2JzLmxkYVssMV0gPiAwLjI1LCAiQiIsICJNIikKcHJlZF9vdXRjb21lc183NSA9IGlmZWxzZShwcm9icy5sZGFbLDFdID4gMC43NSwgIkIiLCAiTSIpCgpgYGAKCmBgYHtyIDJjX3Bsb3QxLCBlY2hvPUZBTFNFLCBvdXQud2lkdGggPSAiNzAlIiwgZmlnLmFsaWduID0gJ2NlbnRlcid9CiMjIFBsb3QgdGhlIHByZWRpY3RlZCBvdXRjb21lcyBmb3IgcHJvYmFiaWxpdHkgY3V0b2ZmIG9mIDAuNQpnZ3Bsb3QoZGF0YSA9IGRlbnNlX3NldCwgYWVzKHggPSBWMywgeSA9IFY0LCBjb2xvciA9IGZhY3RvcihwcmVkX291dGNvbWVzXzA1KSkpICsKICAgICAgICBnZW9tX3BvaW50KGFscGhhID0gMC4yKSArCiAgICAgICAgZ2VvbV9wb2ludChkYXRhID0gdHJhaW5fd2RiYywgYWVzKHggPSBWMywgeSA9IFY0LCBjb2xvciA9IGZhY3RvcihWMikpKSArCiAgICAgICAgc2NhbGVfY29sb3JfZGlzY3JldGUobmFtZSA9ICJQcmVkaWN0aW9uIikgKwogICAgICAgIGdndGl0bGUoIkxEQSBEZWNpc2lvbiBCb3VuZGFyeSAoQ3V0b2ZmID0gMC41KSIpICsKICAgICAgICB4bGFiKCJBdmVyYWdlIHJhZGl1cyIpICsgeWxhYigiQXZlcmFnZSB0ZXh0dXJlIikKYGBgCgpgYGB7ciAyY19wbG90MiwgZWNobz1GQUxTRSwgb3V0LndpZHRoID0gIjcwJSIsIGZpZy5hbGlnbiA9ICdjZW50ZXInfQojIFBsb3QgdGhlIHByZWRpY3RlZCBvdXRjb21lcyBmb3IgYSBwcm9iYWJpbGl0eSBjdXRvZmYgb2YgMC4yNQpnZ3Bsb3QoZGF0YSA9IGRlbnNlX3NldCwgYWVzKHggPSBWMywgeSA9IFY0LCBjb2xvciA9IGZhY3RvcihwcmVkX291dGNvbWVzXzI1KSkpICsKICAgICAgICBnZW9tX3BvaW50KGFscGhhID0gMC4yKSArCiAgICAgICAgZ2VvbV9wb2ludChkYXRhID0gdHJhaW5fd2RiYywgYWVzKHggPSBWMywgeSA9IFY0LCBjb2xvciA9IGZhY3RvcihWMikpKSArCiAgICAgICAgc2NhbGVfY29sb3JfZGlzY3JldGUobmFtZSA9ICJQcmVkaWN0aW9uIikgKwogICAgICAgIGdndGl0bGUoIkxEQSBEZWNpc2lvbiBCb3VuZGFyeSAoQ3V0b2ZmID0gMC4yNSkiKSArCiAgICAgICAgeGxhYigiQXZlcmFnZSByYWRpdXMiKSArIHlsYWIoIkF2ZXJhZ2UgdGV4dHVyZSIpCmBgYAoKYGBge3IgMmNfcGxvdDMsIGVjaG89RkFMU0UsIG91dC53aWR0aCA9ICI3MCUiLCBmaWcuYWxpZ24gPSAnY2VudGVyJ30KIyBQbG90IHRoZSBwcmVkaWN0ZWQgb3V0Y29tZXMgZm9yIGEgcHJvYmFiaWxpdHkgY3V0b2ZmIG9mIDAuNzUKZ2dwbG90KGRhdGEgPSBkZW5zZV9zZXQsIGFlcyh4ID0gVjMsIHkgPSBWNCwgY29sb3IgPSBmYWN0b3IocHJlZF9vdXRjb21lc183NSkpKSArCiAgICAgICAgZ2VvbV9wb2ludChhbHBoYSA9IDAuMikgKwogICAgICAgIGdlb21fcG9pbnQoZGF0YSA9IHRyYWluX3dkYmMsIGFlcyh4ID0gVjMsIHkgPSBWNCwgY29sb3IgPSBmYWN0b3IoVjIpKSkgKwogICAgICAgIHNjYWxlX2NvbG9yX2Rpc2NyZXRlKG5hbWUgPSAiUHJlZGljdGlvbiIpICsKICAgICAgICBnZ3RpdGxlKCJMREEgRGVjaXNpb24gQm91bmRhcnkgKEN1dG9mZiA9IDAuNzUpIikgKwogICAgICAgIHhsYWIoIkF2ZXJhZ2UgcmFkaXVzIikgKyB5bGFiKCJBdmVyYWdlIHRleHR1cmUiKQpgYGAKCioqZC5QbG90IHRoZSBST0MgY3VydmUsIGNvbXB1dGVkIG9uIHRoZSB0ZXN0IHNldC4qKgoKYGBge3IgMmQsIGVjaG89RkFMU0UsIG91dC53aWR0aCA9ICI2MCUiLCBmaWcuYWxpZ24gPSAnY2VudGVyJ30KIyMtLS0tLS0tLS0tLS0tLS0tLS0KIyMgZC4KdGVzdC5sZGFfcHJvYnMgPC0gcHJlZGljdChsZGEud2RiYywgbmV3ZGF0YSA9IHRlc3Rfd2RiYywgIHR5cGUgPSAicmVzcG9uc2UiKSRwb3N0ZXJpb3JbLCAyXQojIyBjYWxjdWxhdGUgQVVDIHNjb3JlCnJvYy5sZGFfc2NvcmUgPSByb2MocmVzcG9uc2UgPSB0ZXN0X3dkYmMkVjIsIHByZWRpY3RvciA9IHRlc3QubGRhX3Byb2JzKSAgCiNyb2MubGRhX3Njb3JlCiMgMC45Mzk2CgojIyBwbG90IHRoZSBST0MgY3VydmUKZ2dyb2Mocm9jLmxkYV9zY29yZSwgbGluZXR5cGU9MSwgc2l6ZSA9IDEpICsgCiAgICAgICAgeGxhYigiRlBSIikgKyB5bGFiKCJUUFIiKSArCiAgICAgICAgZ2d0aXRsZSgiVGVzdCBST0MgY3VydmUgZm9yIExEQSIpCgpgYGAKCioqZS5Db21wdXRlIGFuIGVzdGltYXRlIG9mIHRoZSBBcmVhIHVuZGVyIHRoZSBST0MgQ3VydmUgKEFVQykuKioKClRoZSBBVUMgc2NvcmUgaXMgMC45Mzk2CgpgYGB7ciAyZSwgaW5jbHVkZT1GQUxTRX0Kcm9jLmxkYV9zY29yZQojIDAuOTM5NgpgYGAKCiMjIDMuUXVhZHJhdGljIERpc2NyaW1pbmFudCBBbmFseXNpcyBNb2RlbC4KCioqYS5Ob3cgZml0IGEgbGluZWFyIGRpc2NyaW1pbmFudCBhbmFseXNpcyBtb2RlbCB0byB0aGUgdHJhaW5pbmcgc2V0IHlvdSBjcmVhdGVkIGluIEV4ZXJjaXNlIDEuIE1ha2UgYSB0YWJsZSBkaXNwbGF5aW5nIHRoZSBlc3RpbWF0ZWQg4oCYUHJpb3IgcHJvYmFiaWxpdGllcyBvZiBncm91cHPigJkgYW5kIOKAmEdyb3VwIG1lYW5z4oCZLiBEZXNjcmliZSBpbiB3b3JkcyB0aGUgbWVhbmluZyBvZiB0aGVzZSBlc3RpbWF0ZXMgYW5kIGhvdyB0aGV5IGFyZSByZWxhdGVkIHRvIHRoZSBwb3N0ZXJpb3IgcHJvYmFiaWxpdGllcy4qKgoKVGhlIGVzdGltYXRlZCBncm91cCBtZWFucyBhcmUgdGhlIGF2ZXJhZ2Ugb2YgdGhlIHByZWRpY3RvciB2YXJpYWJsZXMgKHJhZGl1cyBvciB0ZXh0dXJlKSBmb3IgZWFjaCBncm91cCAobWFsaWduYW50IG9yIGJlbmlnbikuIFRoZXNlIGVzdGltYXRlcyBhcmUgdXNlZCB0byBjYWxjdWxhdGUgdGhlIHBvc3RlcmlvciBwcm9iYWJpbGl0aWVzIHdoaWNoIGlzIHRoZSBwcm9iYWJpbGl0eSBvZiBhbiBvYnNlcnZhdGlvbiBiZWxvbmdpbmcgdG8gbWFsaWduYW50IG9yIGJlbmlnbiBnaXZlbiBpdHMgZXN0aW1hdGVkIHByZWRpY3RvciB2YWx1ZXMuIFRoaXMgY2FsY3VsYXRpb24gaXMgYmFzZWQgb24gQmF5ZXMnIHRoZW9yZW0sIHdoaWNoIGNvbWJpbmVzIHByaW9yIGtub3dsZWRnZSAocHJpb3IgcHJvYmFiaWxpdGllcykgYW5kIG5ldyBpbmZvcm1hdGlvbiAoZXN0aW1hdGVkIHByZWRpY3RvciB2YWx1ZXMpIHRvIGVzdGltYXRlIHRoZSBwcm9iYWJpbGl0eSBvZiBhbiBldmVudC4gCgpgYGB7ciAzYSwgcmVzdWx0cz0naGlkZSd9CiMjIGEuCiMjIHF1YWRyYXRpYyBkaXNjcmltaW5hbnQgYW5hbHlzaXMgCnFkYS53ZGJjID0gcWRhKERpZyB+IFYzICsgVjQsIGRhdGEgPSB0cmFpbl93ZGJjLCBjZW50ZXIgPSBUUlVFKQpxZGEud2RiYwpxZGFfdGFibGUgPC0gY2JpbmQocWRhLndkYmMkcHJpb3IsIHFkYS53ZGJjJG1lYW4pCmNvbG5hbWVzKHFkYV90YWJsZSkgPC0gYygiUHJpb3IgcHJvYiBvZiBncm91cHMiLCAiR3JvdXAgbWVhbnMocmFkaXVzKSIsICJHcm91cCBtZWFucyh0ZXh0dXJlKSIpCgpgYGAKCmBgYHtyIDNhXzIgdGFibGV9CmthYmxlKHFkYV90YWJsZSkKYGBgCgoqKmIuVXNlIHRoZSBmaXR0ZWQgbW9kZWwgYW5kIEJheWVzIHJ1bGUgdG8gY29tcHV0ZSB0aGUgcHJlZGljdGVkIG91dGNvbWUgWcuGIGZyb20gdGhlIHByZWRpY3RlZCBwb3N0ZXJpb3IgcHJvYmFiaWxpdGllcywgYm90aCBvbiB0aGUgdHJhaW5pbmcgYW5kIHRlc3Qgc2V0LiBUaGVuLCBjb21wdXRlIHRoZSBjb25mdXNpb24gdGFibGUgYW5kIHByZWRpY3Rpb24gYWNjdXJhY3kgYm90aCBvbiB0aGUgdHJhaW5pbmcgYW5kIHRlc3Qgc2V0LiBDb21tZW50IG9uIHRoZSByZXN1bHRzLioqCgoqIFByZWRpY3Rpb24gYWNjdXJhY3kgb24gdHJhaW5pbmcgc2V0ID0gMC44ODUKCiogUHJlZGljdGlvbiBhY2N1cmFjeSBvbiB0ZXN0aW5nIHNldCA9IDAuODcwCgpUaGUgdHdvIHByZWRpY3Rpb24gYWNjdXJhY3kgYXJlIHNpbWlsYXIsIHdoaWNoIGluZGljYXRlcyB0aGF0IHRoZSBtb2RlbCBmaXRzIHdlbGwuIFRoZSB2YWx1ZXMgYXJlIHNpbWlsYXIgdG8gdGhlIG9uZXMgZnJvbSBMREEuIEl0IHNlZW1zIHRoYXQgdXNpbmcgTERBIG9yIFFEQSBpcyBub3Qgc28gbXVjaCBkaWZmZXJlbnQuIAoKYGBge3IgM2IsIHJlc3VsdHM9J2hpZGUnfQojIy0tLS0tLS0tLS0tLS0tLS0tLQojIyBiLgojIyBmaXQgdGhlIG1vZGVsIGFuZCBjYWxjdWxhdGUgcG9zdCBwcm9iCnRyYWluX3ByZWQucWRhIDwtIHByZWRpY3QocWRhLndkYmMsIFhfdHJhaW4pJHBvc3Rlcmlvcgp0ZXN0X3ByZWQucWRhIDwtIHByZWRpY3QocWRhLndkYmMsIFhfdGVzdCkkcG9zdGVyaW9yCgojIyBjb21wdXRlIG91dGNvbWUgWSBiYXNlZCBvbiB0aGUgcG9zdCBwcm9iIG9uIHRyYWluaW5nIHNldAp0cmFpbl9vdXRjb21lLnFkYSA8LSBhcHBseSh0cmFpbl9wcmVkLnFkYSwgMSwgd2hpY2gubWF4KQojIyBvdXRjb21lIHZhcmlhYmxlID0gMSBvciAyIAojIyB0cmFuc2ZlciB0aGVtIHRvIEIgb3IgTQp0cmFpbl9vdXRjb21lLnFkYSA8LSBkYXRhLmZyYW1lKHRyYWluX291dGNvbWUucWRhKQp0cmFpbl9vdXRjb21lLnFkYSR0cmFpbl9vdXRjb21lLnFkYSA8LSAKICAgICAgICBpZmVsc2UodHJhaW5fb3V0Y29tZS5xZGEkdHJhaW5fb3V0Y29tZS5xZGE9PTEsICJCIiwgIk0iKQoKIyMgY29tcHV0ZSBvdXRjb21lIFkgYmFzZWQgb24gdGhlIHBvc3QgcHJvYiBvbiB0ZXN0aW5nIHNldAp0ZXN0X291dGNvbWUucWRhIDwtIGFwcGx5KHRlc3RfcHJlZC5xZGEsIDEsIHdoaWNoLm1heCkKIyMgb3V0Y29tZSB2YXJpYWJsZSA9IDEgb3IgMiAKIyMgdHJhbnNmZXIgdGhlbSB0byBCIG9yIE0KdGVzdF9vdXRjb21lLnFkYSA8LSBkYXRhLmZyYW1lKHRlc3Rfb3V0Y29tZS5xZGEpCnRlc3Rfb3V0Y29tZS5xZGEkdGVzdF9vdXRjb21lLnFkYSA8LSAKICAgICAgICBpZmVsc2UodGVzdF9vdXRjb21lLnFkYSR0ZXN0X291dGNvbWUucWRhPT0xLCAiQiIsICJNIikKCiMjIGNvbmZ1c2lvbiBtYXRyaXgKdHJhaW5fY29uZnVzaW9uLnFkYSA8LSB0YWJsZShQcmVkaWN0aW9uID0gdHJhaW5fb3V0Y29tZS5xZGEkdHJhaW5fb3V0Y29tZS5xZGEsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIEFjdHVhbCA9IHRyYWluX3dkYmMkVjIpCnRlc3RfY29uZnVzaW9uLnFkYSA8LSB0YWJsZShQcmVkaWN0aW9uID0gdGVzdF9vdXRjb21lLnFkYSR0ZXN0X291dGNvbWUucWRhLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgIEFjdHVhbCA9IHRlc3Rfd2RiYyRWMikKCiMjIHByZWRpY3Rpb24gYWNjdXJhY3kKdHJhaW5fYWNjdXJhY3kucWRhIDwtIHN1bShkaWFnKHRyYWluX2NvbmZ1c2lvbi5xZGEpKSAvIG5yb3codHJhaW5fd2RiYykKdGVzdF9hY2N1cmFjeS5xZGEgPC0gc3VtKGRpYWcodGVzdF9jb25mdXNpb24ucWRhKSkgLyBucm93KHRlc3Rfd2RiYykKCmBgYAoKYGBge3IgM2IgdGFibGUxfQprYWJsZSh0cmFpbl9jb25mdXNpb24ucWRhLAogICAgICBjYXB0aW9uID0gIkNvbmZ1c2lvbiB0YWJsZSBmb3IgdHJhaW5pbmcgc2V0IC0gUURBIikKCmBgYAoKYGBge3IgM2IgdGFibGUyfQprYWJsZSh0ZXN0X2NvbmZ1c2lvbi5xZGEsCiAgICAgIGNhcHRpb24gPSAiQ29uZnVzaW9uIHRhYmxlIGZvciB0ZXN0aW5nIHNldCAtIFFEQSIpCgpgYGAKCioqYy4oYykgUGxvdCBhbiBpbWFnZSBvZiB0aGUgUURBIGRlY2lzaW9uIGJvdW5kYXJ5IChmb2xsb3dpbmcgdGhlIHN0ZXBzIGluIDEoZykpLiBHZW5lcmF0ZSB0aGUgc2FtZSBwbG90IGZvciBjdXRvZmYgdmFsdWVzIG9mIDAuMjUgYW5kIDAuNzUuIENvbW1lbnQgb24gdGhlIHJlc3VsdHMuKioKClRoZSBpbmNyZWFzZSBpbiB0aGUgcmVkIHJlZ2lvbiAocHJlZGljdGVkIGFzIGJlbmlnbiB0dW1vcikgYXMgdGhlIGN1dG9mZiB2YWx1ZSBkZWNyZWFzZXMgV2hlbiB0aGUgY3V0b2ZmIHZhbHVlIGlzIGhpZ2gsIHRoZSBtb2RlbCB3b3VsZCBiZSBtb3JlIGNvbnNlcnZhdGl2ZSBpbiBjbGFzc2lmeWluZyBhbiBvYnNlcnZhdGlvbiBhcyBiZW5pZ24sIHdoaWxlIGFzIHRoZSBjdXRvZmYgdmFsdWUgZGVjcmVhc2UsIHRoZSBtb2RlbCBiZWNvbWVzIG1vcmUgY29uZmlkZW50IGluIGNsYXNzaWZ5aW5nIGFuIG9ic2VydmF0aW9uIGFzIGJlbmlnbi4gCgpgYGB7ciAzYywgcmVzdWx0cz0naGlkZSd9CiMjLS0tLS0tLS0tLS0tLS0tLS0tCiMjIGMuCiMjIHVzZSB0aGUgZml0dGVkIEdMTSBtb2RlbCB0byBwcmVkaWN0IHRoZSBvdXRjb21lIHByb2JhYmlsaXRpZXMgZm9yIGVhY2ggb2JzZXJ2YXRpb24gaW4gdGhlIGRlbnNlIHNldApwcm9icy5xZGEgPC0gcHJlZGljdChxZGEud2RiYywgbmV3ZGF0YSA9IGRlbnNlX3NldCwgdHlwZSA9ICJyZXNwb25zZSIpJHBvc3RlcmlvcgoKIyBDb21wdXRlIHRoZSBwcmVkaWN0ZWQgb3V0Y29tZXMgdXNpbmcgQmF5ZXMgcnVsZSB3aXRoIGRpZmZlcmVudCBwcm9iYWJpbGl0eSBjdXRvZmYgdmFsdWVzCnByZWRfb3V0Y29tZXNfMDUucWRhID0gaWZlbHNlKHByb2JzLnFkYVssMV0gPiBwcm9icy5xZGFbLDJdLCAiQiIsICJNIikKcHJlZF9vdXRjb21lc18yNS5xZGEgPSBpZmVsc2UocHJvYnMucWRhWywxXSA+IDAuMjUsICJCIiwgIk0iKQpwcmVkX291dGNvbWVzXzc1LnFkYSA9IGlmZWxzZShwcm9icy5xZGFbLDFdID4gMC43NSwgIkIiLCAiTSIpCgpgYGAKCmBgYHtyIDNjX3Bsb3QxLCBlY2hvPUZBTFNFLCBvdXQud2lkdGggPSAiNzAlIiwgZmlnLmFsaWduID0gJ2NlbnRlcid9CiMjIFBsb3QgdGhlIHByZWRpY3RlZCBvdXRjb21lcyBmb3IgcHJvYmFiaWxpdHkgY3V0b2ZmIG9mIDAuNQpnZ3Bsb3QoZGF0YSA9IGRlbnNlX3NldCwgYWVzKHggPSBWMywgeSA9IFY0LCBjb2xvciA9IGZhY3RvcihwcmVkX291dGNvbWVzXzA1LnFkYSkpKSArCiAgICAgICAgZ2VvbV9wb2ludChhbHBoYSA9IDAuMikgKwogICAgICAgIGdlb21fcG9pbnQoZGF0YSA9IHRyYWluX3dkYmMsIGFlcyh4ID0gVjMsIHkgPSBWNCwgY29sb3IgPSBmYWN0b3IoVjIpKSkgKwogICAgICAgIHNjYWxlX2NvbG9yX2Rpc2NyZXRlKG5hbWUgPSAiUHJlZGljdGlvbiIpICsKICAgICAgICBnZ3RpdGxlKCJRREEgRGVjaXNpb24gQm91bmRhcnkgKEN1dG9mZiA9IDAuNSkiKSArCiAgICAgICAgeGxhYigiQXZlcmFnZSByYWRpdXMiKSArIHlsYWIoIkF2ZXJhZ2UgdGV4dHVyZSIpCmBgYAoKYGBge3IgM2NfcGxvdDIsIGVjaG89RkFMU0UsIG91dC53aWR0aCA9ICI3MCUiLCBmaWcuYWxpZ24gPSAnY2VudGVyJ30KIyMgUGxvdCB0aGUgcHJlZGljdGVkIG91dGNvbWVzIGZvciBwcm9iYWJpbGl0eSBjdXRvZmYgb2YgMC4yNQpnZ3Bsb3QoZGF0YSA9IGRlbnNlX3NldCwgYWVzKHggPSBWMywgeSA9IFY0LCBjb2xvciA9IGZhY3RvcihwcmVkX291dGNvbWVzXzI1LnFkYSkpKSArCiAgICAgICAgZ2VvbV9wb2ludChhbHBoYSA9IDAuMikgKwogICAgICAgIGdlb21fcG9pbnQoZGF0YSA9IHRyYWluX3dkYmMsIGFlcyh4ID0gVjMsIHkgPSBWNCwgY29sb3IgPSBmYWN0b3IoVjIpKSkgKwogICAgICAgIHNjYWxlX2NvbG9yX2Rpc2NyZXRlKG5hbWUgPSAiUHJlZGljdGlvbiIpICsKICAgICAgICBnZ3RpdGxlKCJRREEgRGVjaXNpb24gQm91bmRhcnkgKEN1dG9mZiA9IDAuMjUpIikgKwogICAgICAgIHhsYWIoIkF2ZXJhZ2UgcmFkaXVzIikgKyB5bGFiKCJBdmVyYWdlIHRleHR1cmUiKQpgYGAKCmBgYHtyIDNjX3Bsb3QzLCBlY2hvPUZBTFNFLCBvdXQud2lkdGggPSAiNzAlIiwgZmlnLmFsaWduID0gJ2NlbnRlcid9CiMjIFBsb3QgdGhlIHByZWRpY3RlZCBvdXRjb21lcyBmb3IgcHJvYmFiaWxpdHkgY3V0b2ZmIG9mIDAuNzUKZ2dwbG90KGRhdGEgPSBkZW5zZV9zZXQsIGFlcyh4ID0gVjMsIHkgPSBWNCwgY29sb3IgPSBmYWN0b3IocHJlZF9vdXRjb21lc183NS5xZGEpKSkgKwogICAgICAgIGdlb21fcG9pbnQoYWxwaGEgPSAwLjIpICsKICAgICAgICBnZW9tX3BvaW50KGRhdGEgPSB0cmFpbl93ZGJjLCBhZXMoeCA9IFYzLCB5ID0gVjQsIGNvbG9yID0gZmFjdG9yKFYyKSkpICsKICAgICAgICBzY2FsZV9jb2xvcl9kaXNjcmV0ZShuYW1lID0gIlByZWRpY3Rpb24iKSArCiAgICAgICAgZ2d0aXRsZSgiUURBIERlY2lzaW9uIEJvdW5kYXJ5IChDdXRvZmYgPSAwLjc1KSIpICsKICAgICAgICB4bGFiKCJBdmVyYWdlIHJhZGl1cyIpICsgeWxhYigiQXZlcmFnZSB0ZXh0dXJlIikKYGBgCgoqKmQuUGxvdCB0aGUgUk9DIGN1cnZlLCBjb21wdXRlZCBvbiB0aGUgdGVzdCBzZXQuKioKCmBgYHtyIDNkLCBlY2hvPUZBTFNFLCBvdXQud2lkdGggPSAiNjAlIiwgZmlnLmFsaWduID0gJ2NlbnRlcid9CiMjLS0tLS0tLS0tLS0tLS0tLS0tCiMjIGQuCnRlc3QucWRhX3Byb2JzIDwtIHByZWRpY3QocWRhLndkYmMsIG5ld2RhdGEgPSB0ZXN0X3dkYmMsIHR5cGUgPSAicmVzcG9uc2UiKSRwb3N0ZXJpb3IKIyMgY2FsY3VsYXRlIEFVQyBzY29yZQpyb2MucWRhX3Njb3JlID0gcm9jKHJlc3BvbnNlID0gdGVzdF93ZGJjJFYyLCBwcmVkaWN0b3IgPSB0ZXN0LnFkYV9wcm9ic1ssIDJdKSAgCiNyb2MucWRhX3Njb3JlCiMgMC45MzYxCgojIyBwbG90IHRoZSBST0MgY3VydmUKZ2dyb2Mocm9jLnFkYV9zY29yZSwgbGluZXR5cGU9MSwgc2l6ZSA9IDEpICsgCiAgICAgICAgeGxhYigiRlBSIikgKyB5bGFiKCJUUFIiKSArCiAgICAgICAgZ2d0aXRsZSgiVGVzdCBST0MgY3VydmUgZm9yIFFEQSIpCgpgYGAKCioqZS5Db21wdXRlIGFuIGVzdGltYXRlIG9mIHRoZSBBcmVhIHVuZGVyIHRoZSBST0MgQ3VydmUgKEFVQykuKioKClRoZSBBVUMgc2NvcmUgaXMgMC45MzYxCgpgYGB7ciAzZSwgaW5jbHVkZT1GQUxTRX0Kcm9jLnFkYV9zY29yZQojIDAuOTM2MQpgYGAKCiMjIDQuS05OIENsYXNzaWZpZXIuCgoqKmEuRm9yIGFsbCBjaG9pY2VzIG9mIGsgPSB7MSwgMiwgMywgNCwgMjB9IChudW1iZXIgb2YgbmVpZ2hib3JzKSwgY29tcHV0ZSB0aGUgcHJlZGljdGVkIG91dGNvbWUgWcuGIGZvciBlYWNoIG9ic2VydmF0aW9uIGluIHRoZSB0cmFpbmluZyBhbmQgdGVzdCBzZXQuIFRoZW4sIGNvbXB1dGUgdGhlIGNvbmZ1c2lvbiB0YWJsZSBhbmQgcHJlZGljdGlvbiBhY2N1cmFjeSBib3RoIG9uIHRoZSB0cmFpbmluZyBhbmQgdGVzdCBzZXQuIENvbW1lbnQgb24gdGhlIHJlc3VsdHMuKioKCmBgYHtyIDRhLCByZXN1bHRzPSdoaWRlJ30KIyMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0gS05OCiMjIGEuCiMjIGtubiAKa192YWx1ZXMgPC0gYygxLCAyLCAzLCA0LCAyMCkKZm9yIChrIGluIGtfdmFsdWVzKSB7CiAgICAgICAgIyB0cmFpbiBzZXQKICAgICAgICBrbm4udHJhaW4ubGFiZWwgPC0ga25uKHRyYWluID0gdHJhaW5fd2RiY1ssYygiVjMiLCAiVjQiKV0sIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGVzdCA9IHRyYWluX3dkYmNbLGMoIlYzIiwgIlY0IildLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNsID0gdHJhaW5fd2RiYyRWMiwgayA9IGspCiAgICAgICAgCiAgICAgICAgIyBjb25mdXNpb24gdGFibGUgCiAgICAgICAgY29uZnVzaW9uX3RhYmxlX3RyYWluIDwtIHRhYmxlKGtubi50cmFpbi5sYWJlbCwgdHJhaW5fd2RiYyRWMikKICAgICAgICAjIHByZWRpY3Rpb24gYWNjdXJhY3kKICAgICAgICBhY2N1cmFjeV90cmFpbiA8LSBzdW0oZGlhZyhjb25mdXNpb25fdGFibGVfdHJhaW4pKSAvIHN1bShjb25mdXNpb25fdGFibGVfdHJhaW4pCiAgICAgICAgCiAgICAgICAgIyB0ZXN0IHNldAogICAgICAgIGtubi50ZXN0LmxhYmVsIDwtIGtubih0cmFpbiA9IHRyYWluX3dkYmNbLGMoIlYzIiwgIlY0IildLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGVzdCA9IHRlc3Rfd2RiY1ssYygiVjMiLCAiVjQiKV0sIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjbCA9IHRyYWluX3dkYmMkVjIsIGsgPSBrKQogICAgICAgIAogICAgICAgICMgY29uZnVzaW9uIHRhYmxlIC0gdGVzdCBzZXQKICAgICAgICBjb25mdXNpb25fdGFibGVfdGVzdCA8LSB0YWJsZShrbm4udGVzdC5sYWJlbCwgdGVzdF93ZGJjJFYyKQogICAgICAgICMgcHJlZGljdGlvbiBhY2N1cmFjeQogICAgICAgIGFjY3VyYWN5X3Rlc3QgPC0gc3VtKGRpYWcoY29uZnVzaW9uX3RhYmxlX3Rlc3QpKSAvIHN1bShjb25mdXNpb25fdGFibGVfdGVzdCkKICAgICAgICAKICAgICAgICAjIFByaW50IHRoZSByZXN1bHRzCiAgICAgICAgY2F0KCJGb3IgayA9IiwgaywgIjpcbiIpCiAgICAgICAgY2F0KCJUcmFpbmluZyBzZXQgY29uZnVzaW9uIHRhYmxlOlxuIikKICAgICAgICBwcmludChjb25mdXNpb25fdGFibGVfdHJhaW4pCiAgICAgICAgY2F0KCJUcmFpbmluZyBzZXQgYWNjdXJhY3k6IiwgYWNjdXJhY3lfdHJhaW4sICJcbiIpCiAgICAgICAgY2F0KCJUZXN0IHNldCBjb25mdXNpb24gdGFibGU6XG4iKQogICAgICAgIHByaW50KGNvbmZ1c2lvbl90YWJsZV90ZXN0KQogICAgICAgIGNhdCgiVGVzdCBzZXQgYWNjdXJhY3k6IiwgYWNjdXJhY3lfdGVzdCwgIlxuXG4iKQp9CgpgYGAKCioqYi5QbG90IGFuIGltYWdlIG9mIHRoZSBkZWNpc2lvbiBib3VuZGFyeSAoZm9sbG93aW5nIHRoZSBzdGVwcyBpbiAxKGcpKSwgZm9yIGsgPSB7MSwgMiwgMywgNCwgMjB9IChudW1iZXIgb2YgbmVpZ2hib3JzKS4gQ29tbWVudCBvbiB0aGUgcmVzdWx0cy4qKgoKQmFzZWQgb24gdGhlIEtOTiBkZWNpc2lvbiBib3VuZGFyeSBwbG90cyB3aXRoIGRpZmZlcmVudCB2YWx1ZXMgb2Ygaywgd2UgY2FuIHNlZSB0aGF0IHRoZSBib3VuZGFyaWVzIGFyZSBsZXNzIHNwZWNpZmljIHRoYW4gdGhlIGJvdW5kYXJpZXMgdXNpbmcgbG9naXN0aWMgcmVncmVzc2lvbiwgTERBLCBhbmQgUURBLiBJdCBtYWtlcyBzZW5zZSBiZWNhdXNlIEtOTiBtb2RlbHMgbWFrZSBwcmVkaWN0aW9ucyBiYXNlZCBvbiB0aGUgY2xhc3NlcyBvZiB0aGUgbmVhcmVzdCBuZWlnaGJvcnMsIHdoZXJlYXMgbG9naXN0aWMgcmVncmVzc2lvbiwgTERBLCBhbmQgUURBIGFyZSBiYXNlZCBvbiBhIG1vcmUgc29waGlzdGljYXRlZCBtYXRoZW1hdGljYWwgbW9kZWwuIEluIGFkZGl0aW9uLCBhY2NvcmRpbmcgdG8gdGhlIGZpdmUgcGxvdHMgd2l0aCBkaWZmZXJlbnQgdmFsdWVzIG9mIGssIHdlIGtub3cgdGhhdCB0aGUgZGVjaXNpb24gYm91bmRhcnkgaXMgbW9yZSBwcmVjaXNlIHdoZW4gaz0zLiBJdCBzZWVtcyB0aGF0IGs9MyBtaWdodCBiZSBtb3JlIGFwcHJvcHJpYXRlIGluIHRoaXMgc2NlbmFyaW8uIAoKYGBge3IgNGJfazEsIGVjaG89RkFMU0UsIG91dC53aWR0aCA9ICI3MCUiLCBmaWcuYWxpZ24gPSAnY2VudGVyJ30KIyMtLS0tLS0tLS0tLS0tLS0tLS0KIyMgYi4KIyMgaz0xCm1vZGVsLmtubi4xID0ga25uKHRyYWluID0gdHJhaW5fd2RiY1ssYygiVjMiLCAiVjQiKV0sIAogICAgICAgICAgICB0ZXN0ID0gZGVuc2Vfc2V0WyxjKCJWMyIsICJWNCIpXSwgCiAgICAgICAgICAgIGNsID0gdHJhaW5fd2RiYyRWMiwgCiAgICAgICAgICAgIGsgPSAxKQoKIyMgUGxvdCAKZ2dwbG90KGRhdGEgPSBkZW5zZV9zZXQsIGFlcyh4ID0gVjMsIHkgPSBWNCwgY29sb3IgPSBmYWN0b3IobW9kZWwua25uLjEpKSkgKwogICAgICAgIGdlb21fcG9pbnQoYWxwaGEgPSAwLjIpICsKICAgICAgICBnZW9tX3BvaW50KGRhdGEgPSB0cmFpbl93ZGJjLCBhZXMoeCA9IFYzLCB5ID0gVjQsIGNvbG9yID0gZmFjdG9yKFYyKSkpICsKICAgICAgICBzY2FsZV9jb2xvcl9kaXNjcmV0ZShuYW1lID0gIlByZWRpY3Rpb24iKSArCiAgICAgICAgZ2d0aXRsZSgiS05OIERlY2lzaW9uIEJvdW5kYXJ5IChrPTEpIikgKwogICAgICAgIHhsYWIoIkF2ZXJhZ2UgcmFkaXVzIikgKyB5bGFiKCJBdmVyYWdlIHRleHR1cmUiKQoKYGBgCgpgYGB7ciA0Yl9rMiwgZWNobz1GQUxTRSwgb3V0LndpZHRoID0gIjcwJSIsIGZpZy5hbGlnbiA9ICdjZW50ZXInfQojIyBrPTIKbW9kZWwua25uLjIgPSBrbm4odHJhaW4gPSB0cmFpbl93ZGJjWyxjKCJWMyIsICJWNCIpXSwgCiAgICAgICAgICAgICAgICAgIHRlc3QgPSBkZW5zZV9zZXRbLGMoIlYzIiwgIlY0IildLCAKICAgICAgICAgICAgICAgICAgY2wgPSB0cmFpbl93ZGJjJFYyLCAKICAgICAgICAgICAgICAgICAgayA9IDIpCgojIyBQbG90IApnZ3Bsb3QoZGF0YSA9IGRlbnNlX3NldCwgYWVzKHggPSBWMywgeSA9IFY0LCBjb2xvciA9IGZhY3Rvcihtb2RlbC5rbm4uMikpKSArCiAgICAgICAgZ2VvbV9wb2ludChhbHBoYSA9IDAuMikgKwogICAgICAgIGdlb21fcG9pbnQoZGF0YSA9IHRyYWluX3dkYmMsIGFlcyh4ID0gVjMsIHkgPSBWNCwgY29sb3IgPSBmYWN0b3IoVjIpKSkgKwogICAgICAgIHNjYWxlX2NvbG9yX2Rpc2NyZXRlKG5hbWUgPSAiUHJlZGljdGlvbiIpICsKICAgICAgICBnZ3RpdGxlKCJLTk4gRGVjaXNpb24gQm91bmRhcnkgKGs9MikiKSArCiAgICAgICAgeGxhYigiQXZlcmFnZSByYWRpdXMiKSArIHlsYWIoIkF2ZXJhZ2UgdGV4dHVyZSIpCgpgYGAKCmBgYHtyIDRiX2szLCBlY2hvPUZBTFNFLCBvdXQud2lkdGggPSAiNzAlIiwgZmlnLmFsaWduID0gJ2NlbnRlcid9CiMjIGs9Mwptb2RlbC5rbm4uMyA9IGtubih0cmFpbiA9IHRyYWluX3dkYmNbLGMoIlYzIiwgIlY0IildLCAKICAgICAgICAgICAgICAgICAgdGVzdCA9IGRlbnNlX3NldFssYygiVjMiLCAiVjQiKV0sIAogICAgICAgICAgICAgICAgICBjbCA9IHRyYWluX3dkYmMkVjIsIAogICAgICAgICAgICAgICAgICBrID0gMykKCiMjIFBsb3QgCmdncGxvdChkYXRhID0gZGVuc2Vfc2V0LCBhZXMoeCA9IFYzLCB5ID0gVjQsIGNvbG9yID0gZmFjdG9yKG1vZGVsLmtubi4zKSkpICsKICAgICAgICBnZW9tX3BvaW50KGFscGhhID0gMC4yKSArCiAgICAgICAgZ2VvbV9wb2ludChkYXRhID0gdHJhaW5fd2RiYywgYWVzKHggPSBWMywgeSA9IFY0LCBjb2xvciA9IGZhY3RvcihWMikpKSArCiAgICAgICAgc2NhbGVfY29sb3JfZGlzY3JldGUobmFtZSA9ICJQcmVkaWN0aW9uIikgKwogICAgICAgIGdndGl0bGUoIktOTiBEZWNpc2lvbiBCb3VuZGFyeSAoaz0zKSIpICsKICAgICAgICB4bGFiKCJBdmVyYWdlIHJhZGl1cyIpICsgeWxhYigiQXZlcmFnZSB0ZXh0dXJlIikKCmBgYAoKYGBge3IgNGJfazQsIGVjaG89RkFMU0UsIG91dC53aWR0aCA9ICI3MCUiLCBmaWcuYWxpZ24gPSAnY2VudGVyJ30KIyMgaz00Cm1vZGVsLmtubi40ID0ga25uKHRyYWluID0gdHJhaW5fd2RiY1ssYygiVjMiLCAiVjQiKV0sIAogICAgICAgICAgICAgICAgICB0ZXN0ID0gZGVuc2Vfc2V0WyxjKCJWMyIsICJWNCIpXSwgCiAgICAgICAgICAgICAgICAgIGNsID0gdHJhaW5fd2RiYyRWMiwgCiAgICAgICAgICAgICAgICAgIGsgPSA0KQoKIyMgUGxvdCAKZ2dwbG90KGRhdGEgPSBkZW5zZV9zZXQsIGFlcyh4ID0gVjMsIHkgPSBWNCwgY29sb3IgPSBmYWN0b3IobW9kZWwua25uLjQpKSkgKwogICAgICAgIGdlb21fcG9pbnQoYWxwaGEgPSAwLjIpICsKICAgICAgICBnZW9tX3BvaW50KGRhdGEgPSB0cmFpbl93ZGJjLCBhZXMoeCA9IFYzLCB5ID0gVjQsIGNvbG9yID0gZmFjdG9yKFYyKSkpICsKICAgICAgICBzY2FsZV9jb2xvcl9kaXNjcmV0ZShuYW1lID0gIlByZWRpY3Rpb24iKSArCiAgICAgICAgZ2d0aXRsZSgiS05OIERlY2lzaW9uIEJvdW5kYXJ5IChrPTQpIikgKwogICAgICAgIHhsYWIoIkF2ZXJhZ2UgcmFkaXVzIikgKyB5bGFiKCJBdmVyYWdlIHRleHR1cmUiKQoKYGBgCgpgYGB7ciA0Yl9rMjAsIGVjaG89RkFMU0UsIG91dC53aWR0aCA9ICI3MCUiLCBmaWcuYWxpZ24gPSAnY2VudGVyJ30KIyMgaz0yMAptb2RlbC5rbm4uMjAgPSBrbm4odHJhaW4gPSB0cmFpbl93ZGJjWyxjKCJWMyIsICJWNCIpXSwgCiAgICAgICAgICAgICAgICAgIHRlc3QgPSBkZW5zZV9zZXRbLGMoIlYzIiwgIlY0IildLCAKICAgICAgICAgICAgICAgICAgY2wgPSB0cmFpbl93ZGJjJFYyLCAKICAgICAgICAgICAgICAgICAgayA9IDIwKQoKIyMgUGxvdCAKZ2dwbG90KGRhdGEgPSBkZW5zZV9zZXQsIGFlcyh4ID0gVjMsIHkgPSBWNCwgY29sb3IgPSBmYWN0b3IobW9kZWwua25uLjIwKSkpICsKICAgICAgICBnZW9tX3BvaW50KGFscGhhID0gMC4yKSArCiAgICAgICAgZ2VvbV9wb2ludChkYXRhID0gdHJhaW5fd2RiYywgYWVzKHggPSBWMywgeSA9IFY0LCBjb2xvciA9IGZhY3RvcihWMikpKSArCiAgICAgICAgc2NhbGVfY29sb3JfZGlzY3JldGUobmFtZSA9ICJQcmVkaWN0aW9uIikgKwogICAgICAgIGdndGl0bGUoIktOTiBEZWNpc2lvbiBCb3VuZGFyeSAoaz0yMCkiKSArCiAgICAgICAgeGxhYigiQXZlcmFnZSByYWRpdXMiKSArIHlsYWIoIkF2ZXJhZ2UgdGV4dHVyZSIpCgpgYGAKCioqYy5Db21wdXRlIGFuZCBwbG90IHRoZSBwcmVkaWN0aW9uIGFjY3VyYWN5IChvbiBib3RoIHRoZSB0cmFpbmluZyBhbmQgdGVzdCBzZXQpLCBmb3IgayA9IHsxLDIsLi4uLDE5LDIwfSAobnVtYmVyIG9mIG5laWdoYm9ycykuIFdoaWNoIHZhbHVlIG9mIGsgd291bGQgeW91IGNob29zZT8gQ29tbWVudCBvbiB0aGUgcmVzdWx0cy4qKgoKSSB3b3VsZCBjaG9vc2Ugaz0zLiBUaGUgZm9sbG93aW5ncyBhcmUgbXkgcmVhc29ucy4gQmFzZWQgb24gdGhlIHBsb3Qgb2YgcHJlZGljdGlvbiBhY2N1cmFjeSBmb3IgZGlmZmVyZW50IHZhbHVlIG9mIGsgb24gdGhlIHRyYWluaW5nIHNldCwgd2UgY2FuIHNlZSB0aGF0IHRoZSBhY2N1cmFjeSBkZWNyZWFzZSBhcyB0aGUgayB2YWx1ZSBpbmNyZWFzZXMuIFdpdGggdGhlIGVsYm93IG1ldGhvZCwgdGhlIGVsYm93IHBvaW50IGlzIGF0IGs9MiBvciBrPTMuIEluIGFkZGl0aW9uLCBiYXNlZCBvbiB0aGUgcHJlZGljdGlvbiBhY2N1cmFjeSBmb3IgZGlmZmVyZW50IHZhbHVlcyBvZiBrIG9uIHRoZSB0ZXN0aW5nLCB3ZSBjYW4gc2VlIHRoYXQgdGhlIGFjY3VyYWN5IGF0IGs9Mywgaz02LCBrPTE3LCBrPTE4LCBrPTE5LCBhbmQgaz0yMCBhcmUgcmVsYXRpdmVseSBoaWdoLiBUaGVyZWZvcmUsIEkgd291bGQgY2hvb3NlIGs9MyBhcyB0aGUgYmVzdCBrIHZhbHVlLgoKYGBge3IgNGMsIHJlc3VsdHM9J2hpZGUnfQojIy0tLS0tLS0tLS0tLS0tLS0tLQojIyBjLgojIyBwbG90IHRoZSBwcmVkaWN0aW9uIGFjY3VyYWN5IAp0cmFpbl9hY2N1cmFjeSA9IG51bWVyaWMoMjApCnRlc3RfYWNjdXJhY3kgPSBudW1lcmljKDIwKQoKZm9yIChpIGluIDE6MjApIHsKICAgICAgICBrbm4ubGFiZWwgPC0ga25uKHRyYWluID0gdHJhaW5fd2RiY1ssYygiVjMiLCJWNCIpXSwgCiAgICAgICAgICAgICAgICAgICAgICAgIHRlc3QgPSB0cmFpbl93ZGJjWyxjKCJWMyIsIlY0IildLCAKICAgICAgICAgICAgICAgICAgICAgICAgY2wgPSB0cmFpbl93ZGJjJFYyLCBrID0gaSkKICAgICAgICB0cmFpbl9hY2N1cmFjeVtpXSA8LSBtZWFuKGtubi5sYWJlbCA9PSB0cmFpbl93ZGJjJFYyKQogICAgICAgIHRlc3RfYWNjdXJhY3lbaV0gPC0gbWVhbihrbm4ubGFiZWwgPT0gdGVzdF93ZGJjJFYyKQp9Cgpmb3IgKGkgaW4gMToyMCkgewogICAgICAgIGtubi5sYWJlbCA8LSBrbm4odHJhaW4gPSB0cmFpbl93ZGJjWyxjKCJWMyIsICJWNCIpXSwgCiAgICAgICAgICAgICAgICAgICAgICAgICB0ZXN0ID0gdGVzdF93ZGJjWyxjKCJWMyIsICJWNCIpXSwgCiAgICAgICAgICAgICAgICAgICAgICAgICBjbCA9IHRyYWluX3dkYmMkVjIsIGsgPSBpKQogICAgICAgIHRlc3RfYWNjdXJhY3lbaV0gPC0gbWVhbihrbm4ubGFiZWwgPT0gdGVzdF93ZGJjJFYyKQp9CgpgYGAKCmBgYHtyIDRjX3AxLCBlY2hvID0gRkFMU0UsIG91dC53aWR0aCA9ICI3MCUiLCBmaWcuYWxpZ24gPSAnY2VudGVyJ30KIyMgdHJhaW4gc2V0CnBsb3QoMToyMCwgdHJhaW5fYWNjdXJhY3ksIHR5cGU9ImwiLCBjb2w9ImJsdWUiLAogICAgIHhsYWI9ImsiLCB5bGFiPSJhY2N1cmFjeSIsCiAgICAgbWFpbj0iUHJlZGljdGlvbiBhY2N1cmFjeSBmb3IgZGlmZmVyZW50IHZhbHVlIG9mIGsgKHRyYWluIHNldCkiKQoKYGBgCgpgYGB7ciA0Y19wMiwgZWNobyA9IEZBTFNFLCBvdXQud2lkdGggPSAiNzAlIiwgZmlnLmFsaWduID0gJ2NlbnRlcid9CiMjIHRlc3Qgc2V0CnBsb3QoMToyMCwgdGVzdF9hY2N1cmFjeSwgdHlwZT0ibCIsIGNvbD0icmVkIiwKICAgICB4bGFiPSJrIiwgeWxhYj0iYWNjdXJhY3kiLAogICAgIG1haW49IlByZWRpY3Rpb24gYWNjdXJhY3kgZm9yIGRpZmZlcmVudCB2YWx1ZSBvZiBrICh0ZXN0IHNldCkiKQoKYGBgCgoK